From 0f7b02c8a663ee0027e2c1a56ecafbe50ce015c7 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Tue, 30 Dec 2025 13:48:35 +0000 Subject: [PATCH] Initial commit --- .gitignore | 1 + CH5xx_ble_firmware_library/BLE/CH58xBLE_LIB.h | 4594 +++++++++++++++++ CH5xx_ble_firmware_library/BLE/LIBCH58xBLE.a | Bin 0 -> 1112814 bytes CH5xx_ble_firmware_library/Ld/Link.ld | 178 + .../RVMSIS/core_riscv.c | 306 ++ .../RVMSIS/core_riscv.h | 381 ++ .../Startup/startup_CH583.S | 188 + .../StdPeriphDriver/CH58x_adc.c | 235 + .../StdPeriphDriver/CH58x_clk.c | 477 ++ .../StdPeriphDriver/CH58x_flash.c | 166 + .../StdPeriphDriver/CH58x_gpio.c | 263 + .../StdPeriphDriver/CH58x_i2c.c | 672 +++ .../StdPeriphDriver/CH58x_pwm.c | 123 + .../StdPeriphDriver/CH58x_pwr.c | 384 ++ .../StdPeriphDriver/CH58x_spi0.c | 368 ++ .../StdPeriphDriver/CH58x_spi1.c | 178 + .../StdPeriphDriver/CH58x_sys.c | 371 ++ .../StdPeriphDriver/CH58x_timer0.c | 75 + .../StdPeriphDriver/CH58x_timer1.c | 104 + .../StdPeriphDriver/CH58x_timer2.c | 104 + .../StdPeriphDriver/CH58x_timer3.c | 75 + .../StdPeriphDriver/CH58x_uart0.c | 151 + .../StdPeriphDriver/CH58x_uart1.c | 150 + .../StdPeriphDriver/CH58x_uart2.c | 151 + .../StdPeriphDriver/CH58x_uart3.c | 151 + .../StdPeriphDriver/CH58x_usb2dev.c | 113 + .../StdPeriphDriver/CH58x_usb2hostBase.c | 652 +++ .../StdPeriphDriver/CH58x_usb2hostClass.c | 826 +++ .../StdPeriphDriver/CH58x_usbhostBase.c | 692 +++ .../StdPeriphDriver/CH58x_usbhostClass.c | 832 +++ .../StdPeriphDriver/inc/CH583SFR.h | 1898 +++++++ .../StdPeriphDriver/inc/CH58x_adc.h | 265 + .../StdPeriphDriver/inc/CH58x_clk.h | 290 ++ .../StdPeriphDriver/inc/CH58x_common.h | 91 + .../StdPeriphDriver/inc/CH58x_flash.h | 42 + .../StdPeriphDriver/inc/CH58x_gpio.h | 274 + .../StdPeriphDriver/inc/CH58x_i2c.h | 180 + .../StdPeriphDriver/inc/CH58x_pwm.h | 152 + .../StdPeriphDriver/inc/CH58x_pwr.h | 167 + .../StdPeriphDriver/inc/CH58x_spi.h | 209 + .../StdPeriphDriver/inc/CH58x_sys.h | 194 + .../StdPeriphDriver/inc/CH58x_timer.h | 555 ++ .../StdPeriphDriver/inc/CH58x_uart.h | 412 ++ .../StdPeriphDriver/inc/CH58x_usbhost.h | 349 ++ .../StdPeriphDriver/inc/ISP583.h | 190 + .../StdPeriphDriver/libISP583.a | Bin 0 -> 5374 bytes COPYING | 97 + Makefile | 60 + README.md | 15 + src/anim.c | 126 + src/ble.c | 200 + src/ble.h | 7 + src/button.c | 79 + src/button.h | 8 + src/cdc.c | 26 + src/cdc.h | 4 + src/img/menu.xbm | 9 + src/led.c | 190 + src/led.h | 24 + src/main.c | 136 + src/menu.c | 145 + src/menu.h | 4 + src/usb/core.c | 232 + src/usb/core.h | 61 + src/usb/ctrl.c | 362 ++ src/wang.c | 184 + src/wang.h | 30 + 67 files changed, 20228 insertions(+) create mode 100644 .gitignore create mode 100644 CH5xx_ble_firmware_library/BLE/CH58xBLE_LIB.h create mode 100644 CH5xx_ble_firmware_library/BLE/LIBCH58xBLE.a create mode 100644 CH5xx_ble_firmware_library/Ld/Link.ld create mode 100644 CH5xx_ble_firmware_library/RVMSIS/core_riscv.c create mode 100644 CH5xx_ble_firmware_library/RVMSIS/core_riscv.h create mode 100644 CH5xx_ble_firmware_library/Startup/startup_CH583.S create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_adc.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_clk.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_flash.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_gpio.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_i2c.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_pwm.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_pwr.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_spi0.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_spi1.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_sys.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer0.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer1.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer2.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer3.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart0.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart1.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart2.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart3.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2dev.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2hostBase.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2hostClass.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbhostBase.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbhostClass.c create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH583SFR.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_adc.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_clk.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_common.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_flash.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_gpio.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_i2c.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_pwm.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_pwr.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_spi.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_sys.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_timer.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_uart.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_usbhost.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/inc/ISP583.h create mode 100644 CH5xx_ble_firmware_library/StdPeriphDriver/libISP583.a create mode 100644 COPYING create mode 100644 Makefile create mode 100644 README.md create mode 100644 src/anim.c create mode 100644 src/ble.c create mode 100644 src/ble.h create mode 100644 src/button.c create mode 100644 src/button.h create mode 100644 src/cdc.c create mode 100644 src/cdc.h create mode 100644 src/img/menu.xbm create mode 100644 src/led.c create mode 100644 src/led.h create mode 100644 src/main.c create mode 100644 src/menu.c create mode 100644 src/menu.h create mode 100644 src/usb/core.c create mode 100644 src/usb/core.h create mode 100644 src/usb/ctrl.c create mode 100644 src/wang.c create mode 100644 src/wang.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/build diff --git a/CH5xx_ble_firmware_library/BLE/CH58xBLE_LIB.h b/CH5xx_ble_firmware_library/BLE/CH58xBLE_LIB.h new file mode 100644 index 0000000..0142f8d --- /dev/null +++ b/CH5xx_ble_firmware_library/BLE/CH58xBLE_LIB.h @@ -0,0 +1,4594 @@ +/********************************** (C) COPYRIGHT ****************************** + * File Name : CH58xBLE_LIB.H + * Author : WCH + * Version : V1.70 + * Date : 2023/01/10 + * Description : head file + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + *******************************************************************************/ + + +/******************************************************************************/ +#ifndef __CH58xBLE_LIB_H +#define __CH58xBLE_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#endif +#include "stdint.h" + +#ifndef int8 +typedef int8_t int8; +#endif +#ifndef int16 +typedef int16_t int16; +#endif +#ifndef BOOL +typedef uint8_t BOOL; +#endif +#ifndef s8 +typedef int8_t s8; +#endif +#ifndef s16 +typedef int16_t s16; +#endif +#ifndef s32 +typedef int32_t s32; +#endif +#ifndef u8 +typedef uint8_t u8; +#endif +#ifndef u16 +typedef uint16_t u16; +#endif +#ifndef u32 +typedef uint32_t u32; +#endif +#ifndef u64 +typedef uint64_t u64; +#endif +#ifndef u8C +typedef const uint8_t u8C; +#endif + +#ifndef u8V +typedef uint8_t volatile u8V; +#endif +#ifndef u32V +typedef uint32_t volatile u32V; +#endif + +#ifndef uint8 +typedef uint8_t uint8; +#endif +#ifndef uint16 +typedef uint16_t uint16; +#endif +#ifndef uint32 +typedef uint32_t uint32; +#endif + +#ifndef TRUE + #define TRUE 1 +#endif +#ifndef FALSE + #define FALSE 0 +#endif +#ifndef NULL + #define NULL 0 +#endif +#ifndef SUCCESS +#define SUCCESS 0x00 +#endif +#ifndef bStatus_t +typedef uint8_t bStatus_t; +#endif +#ifndef tmosTaskID +typedef uint8_t tmosTaskID; +#endif +#ifndef tmosEvents +typedef uint16_t tmosEvents; +#endif +#ifndef tmosTimer +typedef uint32_t tmosTimer; +#endif +#ifndef tmosSnvId_t +typedef uint8_t tmosSnvId_t; +#endif +#ifndef tmosSnvLen_t +typedef uint8_t tmosSnvLen_t; +#endif + +// Define function type that generate a random seed callback +typedef uint32_t (*pfnSrandCB)( void ); +// Define function type that switch to idle mode callback +typedef uint32_t (*pfnIdleCB)( uint32_t ); +// Define function type that run LSI clock calibration callback +typedef void (*pfnLSICalibrationCB)( void ); +// Define function type that get temperature callback +typedef uint16_t (*pfnTempSampleCB)( void ); +// Define function type that connect/advertise event complete callback. +typedef void (*pfnEventCB)( uint32_t timeUs ); +// Define function type that library status callback. +typedef void (*pfnLibStatusErrorCB)( uint8_t code, uint32_t status ); +// Define function type that process event +typedef tmosEvents (*pTaskEventHandlerFn)( tmosTaskID taskID, tmosEvents event ); +// Define function type that read flash +typedef uint32_t (*pfnFlashReadCB)( uint32_t addr, uint32_t num, uint32_t *pBuf ); +// Define function type that write flash +typedef uint32_t (*pfnFlashWriteCB)( uint32_t addr, uint32_t num, uint32_t *pBuf ); +// Define function type that get system clock count +typedef uint32_t (*pfnGetSysClock)( void ); + +/* BLE library config struct */ +typedef struct tag_ble_config +{ + uint32_t MEMAddr; // library memory start address + uint16_t MEMLen; // library memory size + uint32_t SNVAddr; // SNV flash start address( if NULL,bonding information will not be saved ) + uint16_t SNVBlock; // SNV flash block size ( default 256 ) + uint8_t SNVNum; // SNV flash block number ( default 1 ) + uint8_t BufNumber; // Maximum number of sent and received packages cached by the controller( default 5 ) + // Must be greater than the number of connections. + uint16_t BufMaxLen; // Maximum length (in octets) of the data portion of each HCI data packet( default 27 ) + // SC enable,must be greater than 69 + // ATT_MTU = BufMaxLen-4,Range[23,ATT_MAX_MTU_SIZE] + uint8_t TxNumEvent; // Maximum number of TX data in a connection event ( default 1 ) + uint8_t RxNumEvent; // Maximum number of RX data in a connection event ( default equal to BufNumber ) + uint8_t TxPower; // Transmit power level( default LL_TX_POWEER_0_DBM(0dBm) ) + uint8_t WakeUpTime; // Wake up time value in one system count + uint8_t SelRTCClock; // system clock select + // bit0-1 00: LSE(32768Hz) 01:LSI(32000Hz) 10:LSI(32768Hz) + // bit7: 1: ble timer(HSE)(must disable sleep) + uint8_t ConnectNumber; // Connect number,lower two bits are peripheral number,followed by central number + uint8_t WindowWidening; // Wait rf start window(us) + uint8_t WaitWindow; // Wait event arrive window in one system clock + uint8_t MacAddr[6]; // MAC address,little-endian + pfnSrandCB srandCB; // Register a program that generate a random seed + pfnIdleCB sleepCB; // Register a program that set idle + pfnTempSampleCB tsCB; // Register a program that read the current temperature,determine whether calibration is need + pfnLSICalibrationCB rcCB; // Register a program that LSI clock calibration + pfnLibStatusErrorCB staCB; // Register a program that library status callback + pfnFlashReadCB readFlashCB; // Register a program that read flash + pfnFlashWriteCB writeFlashCB; // Register a program that write flash +} bleConfig_t; // Library initialization call BLE_LibInit function + +/* BLE pa control config struct */ +typedef struct tag_ble_pa_control_config +{ + uint32_t txEnableGPIO; // tx enable gpio register + uint32_t txDisableGPIO; // tx disable gpio register + uint32_t tx_pin; // tx pin define + uint32_t rxEnableGPIO; // rx enable gpio register + uint32_t rxDisableGPIO; // rx disable gpio register + uint32_t rx_pin; // tx pin define +} blePaControlConfig_t; + +// defined for all task +#define SYS_EVENT_MSG (0x8000) // A message is waiting event +#define INVALID_TASK_ID 0xFF // Task ID isn't setup properly +#define TASK_NO_TASK 0xFF + +typedef struct +{ + uint8_t event; + uint8_t status; +} tmos_event_hdr_t; + +/********************************************************************* + * GLOBAL MACROS + */ +#define VER_FILE "CH58x_BLE_LIB_V1.7" +extern const uint8_t VER_LIB[]; // LIB version +#define SYSTEM_TIME_MICROSEN 625 // unit of process event timer is 625us +#define MS1_TO_SYSTEM_TIME(x) ((x)*1000/SYSTEM_TIME_MICROSEN) // transform unit in ms to unit in 625us ( attentional bias ) +#define TMOS_TIME_VALID (30*1000*1000) // the maximum task time = RTC MAX clock - TMOS_TIME_VALID + +/* takes a byte out of a uint32_t : var - uint32_t, ByteNum - byte to take out (0 - 3) */ +#define BREAK_UINT32( var, ByteNum ) (uint8_t)((uint32_t)(((var) >>((ByteNum) * 8)) & 0x00FF)) +#define HI_UINT16(a) (((a) >> 8) & 0xFF) +#define LO_UINT16(a) ((a) & 0xFF) +#define HI_UINT8(a) (((a) >> 4) & 0x0F) +#define LO_UINT8(a) ((a) & 0x0F) +#define BUILD_UINT32(Byte0, Byte1, Byte2, Byte3) \ + ((uint32_t)(((uint32_t)(Byte0) & 0x00FF) \ + + (((uint32_t)(Byte1) & 0x00FF) << 8) \ + + (((uint32_t)(Byte2) & 0x00FF) << 16) \ + + (((uint32_t)(Byte3) & 0x00FF) << 24))) +#define BUILD_UINT16(loByte, hiByte) ((uint16_t)(((loByte) & 0x00FF)|(((hiByte) & 0x00FF)<<8))) + +#define ACTIVE_LOW ! +#define ACTIVE_HIGH !! // double negation forces result to be '1' + +#ifndef BV +#define BV(n) (1 << (n)) +#endif + +#ifndef BF +#define BF(x,b,s) (((x) & (b)) >> (s)) +#endif + +#ifndef MIN +#define MIN(n,m) (((n) < (m)) ? (n) : (m)) +#endif + +#ifndef MAX +#define MAX(n,m) (((n) < (m)) ? (m) : (n)) +#endif + +#ifndef ABS +#define ABS(n) (((n) < 0) ? -(n) : (n)) +#endif + +/* TxPower define(Accuracy:��2dBm) */ +#define LL_TX_POWEER_MINUS_16_DBM 0x01 +#define LL_TX_POWEER_MINUS_12_DBM 0x02 +#define LL_TX_POWEER_MINUS_8_DBM 0x04 +#define LL_TX_POWEER_MINUS_5_DBM 0x07 +#define LL_TX_POWEER_MINUS_3_DBM 0x09 +#define LL_TX_POWEER_MINUS_1_DBM 0x0B +#define LL_TX_POWEER_0_DBM 0x0D +#define LL_TX_POWEER_1_DBM 0x0F +#define LL_TX_POWEER_2_DBM 0x13 +#define LL_TX_POWEER_3_DBM 0x17 +#define LL_TX_POWEER_4_DBM 0x1D +#define LL_TX_POWEER_5_DBM 0x29 +#define LL_TX_POWEER_6_DBM 0x3D + +/* ERR_LIB_INIT define */ +#define ERR_LLE_IRQ_HANDLE 0x01 +#define ERR_MEM_ALLOCATE_SIZE 0x02 +#define ERR_SET_MAC_ADDR 0x03 +#define ERR_GAP_ROLE_CONFIG 0x04 +#define ERR_CONNECT_NUMBER_CONFIG 0x05 +#define ERR_SNV_ADDR_CONFIG 0x06 +#define ERR_CLOCK_SELECT_CONFIG 0x07 + +//! Default Public and Random Address Length +#define B_ADDR_LEN 6 +//! Random Number Size +#define B_RANDOM_NUM_SIZE 8 +//! Default key length +#define KEYLEN 16 +#define PUBLIC_KEY_LEN 64 + +//! Maximum Advertising Packet Length +#define B_MAX_ADV_LEN 31 // maximum legacy advertising packet length +#define B_MAX_ADV_EXT_LEN 460 // maximum extended advertising packet length +#define B_MAX_ADV_PERIODIC_LEN 460 // maximum periodic advertising packet length + +#define FAILURE 0x01 //!< Failure +#define INVALIDPARAMETER 0x02 //!< Invalid request field +#define INVALID_TASK 0x03 //!< Task ID isn't setup properly +#define MSG_BUFFER_NOT_AVAIL 0x04 //!< No buffer is available. +#define INVALID_MSG_POINTER 0x05 //!< No message pointer. +#define INVALID_EVENT_ID 0x06 //!< Invalid event id. +#define INVALID_TIMEOUT 0x07 //!< Invalid timeout. +#define NO_TIMER_AVAIL 0x08 //!< No event is available. +#define NV_OPER_FAILED 0x0A //!< read a data item to NV failed. +#define INVALID_MEM_SIZE 0x0B //!< The tokens take up too much space and don't fit into Advertisement data and Scan Response Data + +/** BLE_STATUS_VALUES BLE Default BLE Status Values + * returned as bStatus_t + */ +#define bleInvalidTaskID INVALID_TASK //!< Task ID isn't setup properly +#define bleEecKeyRequestRejected 0x06 //!< key missing +#define bleNotReady 0x10 //!< Not ready to perform task +#define bleAlreadyInRequestedMode 0x11 //!< Already performing that task +#define bleIncorrectMode 0x12 //!< Not setup properly to perform that task +#define bleMemAllocError 0x13 //!< Memory allocation error occurred +#define bleNotConnected 0x14 //!< Can't perform function when not in a connection +#define bleNoResources 0x15 //!< There are no resource available +#define blePending 0x16 //!< Waiting +#define bleTimeout 0x17 //!< Timed out performing function +#define bleInvalidRange 0x18 //!< A parameter is out of range +#define bleLinkEncrypted 0x19 //!< The link is already encrypted +#define bleProcedureComplete 0x1A //!< The Procedure is completed +#define bleInvalidMtuSize 0x1B //!< SDU size is larger than peer MTU. + +/********************************LinkDB****************************************/ +// Special case connection handles +#define INVALID_CONNHANDLE 0xFFFF // Invalid connection handle, used for no connection handle +#define LOOPBACK_CONNHANDLE 0xFFFE // Loopback connection handle, used to loopback a message +// Link state flags +#define LINK_NOT_CONNECTED 0x00 // Link isn't connected +#define LINK_CONNECTED 0x01 // Link is connected +#define LINK_AUTHENTICATED 0x02 // Link is authenticated +#define LINK_BOUND 0x04 // Link is bonded +#define LINK_ENCRYPTED 0x10 // Link is encrypted +// Link Database Status callback changeTypes +#define LINKDB_STATUS_UPDATE_NEW 0 // New connection created +#define LINKDB_STATUS_UPDATE_REMOVED 1 // Connection was removed +#define LINKDB_STATUS_UPDATE_STATEFLAGS 2 // Connection state flag changed +/*******************************gattUUID***************************************/ +/** + * GATT Services + */ +#define GAP_SERVICE_UUID 0x1800 // Generic Access Profile +#define GATT_SERVICE_UUID 0x1801 // Generic Attribute Profile + +/** + * GATT Declarations + */ +#define GATT_PRIMARY_SERVICE_UUID 0x2800 // Primary Service +#define GATT_SECONDARY_SERVICE_UUID 0x2801 // Secondary Service +#define GATT_INCLUDE_UUID 0x2802 // Include +#define GATT_CHARACTER_UUID 0x2803 // Characteristic + +/** + * GATT Descriptors + */ +#define GATT_CHAR_EXT_PROPS_UUID 0x2900 // Characteristic Extended Properties +#define GATT_CHAR_USER_DESC_UUID 0x2901 // Characteristic User Description +#define GATT_CLIENT_CHAR_CFG_UUID 0x2902 // Client Characteristic Configuration +#define GATT_SERV_CHAR_CFG_UUID 0x2903 // Server Characteristic Configuration +#define GATT_CHAR_FORMAT_UUID 0x2904 // Characteristic Presentation Format +#define GATT_CHAR_AGG_FORMAT_UUID 0x2905 // Characteristic Aggregate Format +#define GATT_VALID_RANGE_UUID 0x2906 // Valid Range +#define GATT_EXT_REPORT_REF_UUID 0x2907 // External Report Reference Descriptor +#define GATT_REPORT_REF_UUID 0x2908 // Report Reference Descriptor + +/** + * GATT Characteristics + */ +#define DEVICE_NAME_UUID 0x2A00 // Device Name +#define APPEARANCE_UUID 0x2A01 // Appearance +#define PERI_PRIVACY_FLAG_UUID 0x2A02 // Peripheral Privacy Flag +#define RECONNECT_ADDR_UUID 0x2A03 // Reconnection Address +#define PERI_CONN_PARAM_UUID 0x2A04 // Peripheral Preferred Connection Parameters +#define SERVICE_CHANGED_UUID 0x2A05 // Service Changed +#define CENTRAL_ADDRESS_RESOLUTION_UUID 0x2AA6 // Central Address Resolution + +/** + * GATT Service UUIDs + */ +#define IMMEDIATE_ALERT_SERV_UUID 0x1802 // Immediate Alert +#define LINK_LOSS_SERV_UUID 0x1803 // Link Loss +#define TX_PWR_LEVEL_SERV_UUID 0x1804 // Tx Power +#define CURRENT_TIME_SERV_UUID 0x1805 // Current Time Service +#define REF_TIME_UPDATE_SERV_UUID 0x1806 // Reference Time Update Service +#define NEXT_DST_CHANGE_SERV_UUID 0x1807 // Next DST Change Service +#define GLUCOSE_SERV_UUID 0x1808 // Glucose +#define THERMOMETER_SERV_UUID 0x1809 // Health Thermometer +#define DEVINFO_SERV_UUID 0x180A // Device Information +#define NWA_SERV_UUID 0x180B // Network Availability +#define HEARTRATE_SERV_UUID 0x180D // Heart Rate +#define PHONE_ALERT_STS_SERV_UUID 0x180E // Phone Alert Status Service +#define BATT_SERV_UUID 0x180F // Battery Service +#define BLOODPRESSURE_SERV_UUID 0x1810 // Blood Pressure +#define ALERT_NOTIF_SERV_UUID 0x1811 // Alert Notification Service +#define HID_SERV_UUID 0x1812 // Human Interface Device +#define SCAN_PARAM_SERV_UUID 0x1813 // Scan Parameters +#define RSC_SERV_UUID 0x1814 // Running Speed and Cadence +#define CSC_SERV_UUID 0x1816 // Cycling Speed and Cadence +#define CYCPWR_SERV_UUID 0x1818 // Cycling Power +#define LOC_NAV_SERV_UUID 0x1819 // Location and Navigation + +/** + * GATT Characteristic UUIDs + */ +#define ALERT_LEVEL_UUID 0x2A06 // Alert Level +#define TX_PWR_LEVEL_UUID 0x2A07 // Tx Power Level +#define DATE_TIME_UUID 0x2A08 // Date Time +#define DAY_OF_WEEK_UUID 0x2A09 // Day of Week +#define DAY_DATE_TIME_UUID 0x2A0A // Day Date Time +#define EXACT_TIME_256_UUID 0x2A0C // Exact Time 256 +#define DST_OFFSET_UUID 0x2A0D // DST Offset +#define TIME_ZONE_UUID 0x2A0E // Time Zone +#define LOCAL_TIME_INFO_UUID 0x2A0F // Local Time Information +#define TIME_WITH_DST_UUID 0x2A11 // Time with DST +#define TIME_ACCURACY_UUID 0x2A12 // Time Accuracy +#define TIME_SOURCE_UUID 0x2A13 // Time Source +#define REF_TIME_INFO_UUID 0x2A14 // Reference Time Information +#define TIME_UPDATE_CTRL_PT_UUID 0x2A16 // Time Update Control Point +#define TIME_UPDATE_STATE_UUID 0x2A17 // Time Update State +#define GLUCOSE_MEAS_UUID 0x2A18 // Glucose Measurement +#define BATT_LEVEL_UUID 0x2A19 // Battery Level +#define TEMP_MEAS_UUID 0x2A1C // Temperature Measurement +#define TEMP_TYPE_UUID 0x2A1D // Temperature Type +#define IMEDIATE_TEMP_UUID 0x2A1E // Intermediate Temperature +#define MEAS_INTERVAL_UUID 0x2A21 // Measurement Interval +#define BOOT_KEY_INPUT_UUID 0x2A22 // Boot Keyboard Input Report +#define SYSTEM_ID_UUID 0x2A23 // System ID +#define MODEL_NUMBER_UUID 0x2A24 // Model Number String +#define SERIAL_NUMBER_UUID 0x2A25 // Serial Number String +#define FIRMWARE_REV_UUID 0x2A26 // Firmware Revision String +#define HARDWARE_REV_UUID 0x2A27 // Hardware Revision String +#define SOFTWARE_REV_UUID 0x2A28 // Software Revision String +#define MANUFACTURER_NAME_UUID 0x2A29 // Manufacturer Name String +#define IEEE_11073_CERT_DATA_UUID 0x2A2A // IEEE 11073-20601 Regulatory Certification Data List +#define CURRENT_TIME_UUID 0x2A2B // Current Time +#define SCAN_REFRESH_UUID 0x2A31 // Scan Refresh +#define BOOT_KEY_OUTPUT_UUID 0x2A32 // Boot Keyboard Output Report +#define BOOT_MOUSE_INPUT_UUID 0x2A33 // Boot Mouse Input Report +#define GLUCOSE_CONTEXT_UUID 0x2A34 // Glucose Measurement Context +#define BLOODPRESSURE_MEAS_UUID 0x2A35 // Blood Pressure Measurement +#define IMEDIATE_CUFF_PRESSURE_UUID 0x2A36 // Intermediate Cuff Pressure +#define HEARTRATE_MEAS_UUID 0x2A37 // Heart Rate Measurement +#define BODY_SENSOR_LOC_UUID 0x2A38 // Body Sensor Location +#define HEARTRATE_CTRL_PT_UUID 0x2A39 // Heart Rate Control Point +#define NETWORK_AVAIL_UUID 0x2A3E // Network Availability +#define ALERT_STATUS_UUID 0x2A3F // Alert Status +#define RINGER_CTRL_PT_UUID 0x2A40 // Ringer Control Point +#define RINGER_SETTING_UUID 0x2A41 // Ringer Setting +#define ALERT_CAT_ID_BMASK_UUID 0x2A42 // Alert Category ID Bit Mask +#define ALERT_CAT_ID_UUID 0x2A43 // Alert Category ID +#define ALERT_NOTIF_CTRL_PT_UUID 0x2A44 // Alert Notification Control Point +#define UNREAD_ALERT_STATUS_UUID 0x2A45 // Unread Alert Status +#define NEW_ALERT_UUID 0x2A46 // New Alert +#define SUP_NEW_ALERT_CAT_UUID 0x2A47 // Supported New Alert Category +#define SUP_UNREAD_ALERT_CAT_UUID 0x2A48 // Supported Unread Alert Category +#define BLOODPRESSURE_FEATURE_UUID 0x2A49 // Blood Pressure Feature +#define HID_INFORMATION_UUID 0x2A4A // HID Information +#define REPORT_MAP_UUID 0x2A4B // Report Map +#define HID_CTRL_PT_UUID 0x2A4C // HID Control Point +#define REPORT_UUID 0x2A4D // Report +#define PROTOCOL_MODE_UUID 0x2A4E // Protocol Mode +#define SCAN_INTERVAL_WINDOW_UUID 0x2A4F // Scan Interval Window +#define PNP_ID_UUID 0x2A50 // PnP ID +#define GLUCOSE_FEATURE_UUID 0x2A51 // Glucose Feature +#define RECORD_CTRL_PT_UUID 0x2A52 // Record Access Control Point +#define RSC_MEAS_UUID 0x2A53 // RSC Measurement +#define RSC_FEATURE_UUID 0x2A54 // RSC Feature +#define SC_CTRL_PT_UUID 0x2A55 // SC Control Point +#define CSC_MEAS_UUID 0x2A5B // CSC Measurement +#define CSC_FEATURE_UUID 0x2A5C // CSC Feature +#define SENSOR_LOC_UUID 0x2A5D // Sensor Location +#define CYCPWR_MEAS_UUID 0x2A63 // Cycling Power Measurement +#define CYCPWR_VECTOR_UUID 0x2A64 // Cycling Power Vector +#define CYCPWR_FEATURE_UUID 0x2A65 // Cycling Power Feature +#define CYCPWR_CTRL_PT_UUID 0x2A66 // Cycling Power Control Point +#define LOC_SPEED_UUID 0x2A67 // Location and Speed +#define NAV_UUID 0x2A68 // Navigation +#define POS_QUALITY_UUID 0x2A69 // Position Quality +#define LN_FEATURE_UUID 0x2A6A // LN Feature +#define LN_CTRL_PT_UUID 0x2A6B // LN Control Point +#define ELE_UUID 0x2A6C // Elevation +#define PRESSURE_UUID 0x2A6D // Pressure +#define TEMP_UUID 0x2A6E // Temperature +#define HUMI_UUID 0x2A6F // Humidity +#define TRUE_WIND_SPEED_UUID 0x2A70 // True Wind Speed +#define TRUE_WIND_DIRECTION_UUID 0x2A71 // True Wind Direction +#define URI_UUID 0x2AB6 // URI +#define MEDIA_STATE_UUID 0x2BA3 // Media State +#define MEDIA_CTRL_PT_UUID 0x2BA4 // Media Control Point +#define MEDIA_CTRL_PT_OS_UUID 0x2BA5 // Media Control Point Opcodes Supported +#define CALL_STATE_UUID 0x2BBD // Call State +#define CALL_CTRL_PT_UUID 0x2BBE // Call Control Point +#define CALL_CTRL_PT_OO_UUID 0x2BBF // Call Control Point Optional Opcodes +#define TERM_REASON_UUID 0x2BC0 // Termination Reason +#define INCOMING_CALL_UUID 0x2BC1 // Incoming Call +#define MUTE_UUID 0x2BC3 // Mute + +/** + * GATT Unit UUIDs + */ +#define GATT_UNITLESS_UUID 0x2700 // unitless +#define GATT_UNIT_LENGTH_METER_UUID 0x2701 // m, m +#define GATT_UNIT_MASS_KGRAM_UUID 0x2702 // kg, kg +#define GATT_UNIT_TIME_SECOND_UUID 0x2703 // s, s +#define GATT_UNIT_ELECTRIC_CURRENT_A_UUID 0x2704 // A, A +#define GATT_UNIT_THERMODYN_TEMP_K_UUID 0x2705 // K, K +#define GATT_UNIT_AMOUNT_SUBSTANCE_M_UUID 0x2706 // mol, mol +#define GATT_UNIT_LUMINOUS_INTENSITY_C_UUID 0x2707 // cd, cd + +#define GATT_UNIT_AREA_SQ_MTR_UUID 0x2710 // m^2, m^2 +#define GATT_UNIT_VOLUME_CUBIC_MTR_UUID 0x2711 // m^3, m^3 +#define GATT_UNIT_VELOCITY_MPS_UUID 0x2712 // m/s, m s^-1 +#define GATT_UNIT_ACCELERATION_MPS_SQ_UUID 0x2713 // m/s^2, m s^-2 +#define GATT_UNIT_WAVENUMBER_RM_UUID 0x2714 // ? m^-1 +#define GATT_UNIT_DENSITY_KGPCM_UUID 0x2715 // p, kg m^-3 +#define GATT_UNIT_SURFACE_DENSITY_KGPSM_UUID 0x2716 // pA, kg m^-2 +#define GATT_UNIT_SPECIFIC_VOLUME_CMPKG_UUID 0x2717 // v, m^3 kg^-1 +#define GATT_UNIT_CURRENT_DENSITY_APSM_UUID 0x2718 // j, A m^-2 +#define GATT_UNIT_MAG_FIELD_STRENGTH_UUID 0x2719 // H, A m +#define GATT_UNIT_AMOUNT_CONC_MPCM_UUID 0x271A // c, mol m^-3 +#define GATT_UNIT_MASS_CONC_KGPCM_UUID 0x271B // c, kg m^-3 +#define GATT_UNIT_LUMINANCE_CPSM_UUID 0x271C // Lv, cd m^-2 +#define GATT_UNIT_REFRACTIVE_INDEX_UUID 0x271D // n, 1 +#define GATT_UNIT_RELATIVE_PERMEABLILTY_UUID 0x271E // u, 1 +#define GATT_UNIT_PLANE_ANGLE_RAD_UUID 0x2720 // rad, m m-1 +#define GATT_UNIT_SOLID_ANGLE_STERAD_UUID 0x2721 // sr, m2 m-2 +#define GATT_UNIT_FREQUENCY_HTZ_UUID 0x2722 // Hz, s-1 +#define GATT_UNIT_FORCE_NEWTON_UUID 0x2723 // N, m kg s-2 +#define GATT_UNIT_PRESSURE_PASCAL_UUID 0x2724 // Pa, N/m2 = m2 kg s-2 +#define GATT_UNIT_ENERGY_JOULE_UUID 0x2725 // J, N m = m2 kg s-2 +#define GATT_UNIT_POWER_WATT_UUID 0x2726 // W, J/s = m2 kg s-3 +#define GATT_UNIT_E_CHARGE_C_UUID 0x2727 // C, sA +#define GATT_UNIT_E_POTENTIAL_DIF_V_UUID 0x2728 // V, W/A = m2 kg s-3 A-1 + +#define GATT_UNIT_CELSIUS_TEMP_DC_UUID 0x272F // oC, t/oC = T/K - 273.15 + +#define GATT_UNIT_TIME_MINUTE_UUID 0x2760 // min, 60 s +#define GATT_UNIT_TIME_HOUR_UUID 0x2761 // h, 3600 s +#define GATT_UNIT_TIME_DAY_UUID 0x2762 // d, 86400 s +#define GATT_UNIT_PLANE_ANGLE_DEGREE_UUID 0x2763 // o, (pi/180) rad +#define GATT_UNIT_PLANE_ANGLE_MINUTE_UUID 0x2764 // ', (pi/10800) rad +#define GATT_UNIT_PLANE_ANGLE_SECOND_UUID 0x2765 // '', (pi/648000) rad +#define GATT_UNIT_AREA_HECTARE_UUID 0x2766 // ha, 10^4 m^2 +#define GATT_UNIT_VOLUME_LITRE_UUID 0x2767 // l, 10^-3 m^3 +#define GATT_UNIT_MASS_TONNE_UUID 0x2768 // t, 10^3 kg + +#define GATT_UINT_LENGTH_YARD_UUID 0x27A0 // yd, 0.9144 m +#define GATT_UNIT_LENGTH_PARSEC_UUID 0x27A1 // pc, 3.085678 ?1016 m +#define GATT_UNIT_LENGTH_INCH_UUID 0x27A2 // in, 0.0254 m +#define GATT_UNIT_LENGTH_FOOT_UUID 0x27A3 // ft, 0.3048 m +#define GATT_UNIT_LENGTH_MILE_UUID 0x27A4 // mi, 1609.347 m +#define GATT_UNIT_PRESSURE_PFPSI_UUID 0x27A5 // psi, 6.894757 ?103 Pa +#define GATT_UNIT_VELOCITY_KMPH_UUID 0x27A6 // km/h, 0.2777778 m^s-1 +#define GATT_UNIT_VELOCITY_MPH_UUID 0x27A7 // mi/h, 0.44704 m^ s-1 +#define GATT_UNIT_ANGULAR_VELOCITY_RPM_UUID 0x27A8 // r/min, 0.1047198 rad s-1 +#define GATT_UNIT_ENERGY_GCAL_UUID 0x27A9 // energy (gram calorie) +#define GATT_UNIT_ENERGY_KCAL_UUID 0x27AA // kcal, 4190.02 J +#define GATT_UNIT_ENERGY_KWH_UUID 0x27AB // kWh, 3600000 J +#define GATT_UNIT_THERMODYN_TEMP_DF_UUID 0x27AC // oF, t/oF = T/K ?1.8 - 459.67 +#define GATT_UNIT_PERCENTAGE_UUID 0x27AD // percentage,% +#define GATT_UNIT_PER_MILE_UUID 0x27AE // per mille +#define GATT_UNIT_PERIOD_BPM_UUID 0x27AF // period (beats per minute),BPM +#define GATT_UNIT_E_CHARGE_AH_UUID 0x27B0 // electric charge (ampere hours) +#define GATT_UNIT_MASS_DENSITY_MGPD_UUID 0x27B1 // mass density (milligram per decilitre) +#define GATT_UNIT_MASS_DENSITY_MMPL_UUID 0x27B2 // mass density (millimole per litre) +#define GATT_UNIT_TIME_YEAR_UUID 0x27B3 // time (year) +#define GATT_UNIT_TIME_MONTH_UUID 0x27B4 // time (month) + +/*********************************Messages IDs*********************************/ +// GATT - Messages IDs +#define GATT_MSG_EVENT 0xB0 //!< Incoming GATT message +#define GATT_SERV_MSG_EVENT 0xB1 //!< Incoming GATT ServApp message +// GAP - Messages IDs +#define GAP_MSG_EVENT 0xD0 //!< Incoming GAP message +/************************************ATT***************************************/ +#define ATT_MTU_SIZE 23 //!< Minimum ATT MTU size +#define ATT_MAX_MTU_SIZE 512 //!< Maximum ATT MTU size +// ATT Methods +#define ATT_ERROR_RSP 0x01 //!< ATT Error Response +#define ATT_EXCHANGE_MTU_REQ 0x02 //!< ATT Exchange MTU Request +#define ATT_EXCHANGE_MTU_RSP 0x03 //!< ATT Exchange MTU Response +#define ATT_FIND_INFO_REQ 0x04 //!< ATT Find Information Request +#define ATT_FIND_INFO_RSP 0x05 //!< ATT Find Information Response +#define ATT_FIND_BY_TYPE_VALUE_REQ 0x06 //!< ATT Find By Type Value Request +#define ATT_FIND_BY_TYPE_VALUE_RSP 0x07 //!< ATT Find By Type Value Response +#define ATT_READ_BY_TYPE_REQ 0x08 //!< ATT Read By Type Request +#define ATT_READ_BY_TYPE_RSP 0x09 //!< ATT Read By Type Response +#define ATT_READ_REQ 0x0a //!< ATT Read Request +#define ATT_READ_RSP 0x0b //!< ATT Read Response +#define ATT_READ_BLOB_REQ 0x0c //!< ATT Read Blob Request +#define ATT_READ_BLOB_RSP 0x0d //!< ATT Read Blob Response +#define ATT_READ_MULTI_REQ 0x0e //!< ATT Read Multiple Request +#define ATT_READ_MULTI_RSP 0x0f //!< ATT Read Multiple Response +#define ATT_READ_BY_GRP_TYPE_REQ 0x10 //!< ATT Read By Group Type Request +#define ATT_READ_BY_GRP_TYPE_RSP 0x11 //!< ATT Read By Group Type Response +#define ATT_WRITE_REQ 0x12 //!< ATT Write Request +#define ATT_WRITE_RSP 0x13 //!< ATT Write Response +#define ATT_PREPARE_WRITE_REQ 0x16 //!< ATT Prepare Write Request +#define ATT_PREPARE_WRITE_RSP 0x17 //!< ATT Prepare Write Response +#define ATT_EXECUTE_WRITE_REQ 0x18 //!< ATT Execute Write Request +#define ATT_EXECUTE_WRITE_RSP 0x19 //!< ATT Execute Write Response +#define ATT_HANDLE_VALUE_NOTI 0x1b //!< ATT Handle Value Notification +#define ATT_HANDLE_VALUE_IND 0x1d //!< ATT Handle Value Indication +#define ATT_HANDLE_VALUE_CFM 0x1e //!< ATT Handle Value Confirmation + +#define ATT_WRITE_CMD 0x52 //!< ATT Write Command +#define ATT_SIGNED_WRITE_CMD 0xD2 //!< ATT Signed Write Command + +// ATT Error Codes +#define ATT_ERR_INVALID_HANDLE 0x01 //!< The attribute handle given was not valid on this server +#define ATT_ERR_READ_NOT_PERMITTED 0x02 //!< The attribute cannot be read +#define ATT_ERR_WRITE_NOT_PERMITTED 0x03 //!< The attribute cannot be written +#define ATT_ERR_INVALID_PDU 0x04 //!< The attribute PDU was invalid +#define ATT_ERR_INSUFFICIENT_AUTHEN 0x05 //!< The attribute requires authentication before it can be read or written +#define ATT_ERR_UNSUPPORTED_REQ 0x06 //!< Attribute server does not support the request received from the client +#define ATT_ERR_INVALID_OFFSET 0x07 //!< Offset specified was past the end of the attribute +#define ATT_ERR_INSUFFICIENT_AUTHOR 0x08 //!< The attribute requires authorization before it can be read or written +#define ATT_ERR_PREPARE_QUEUE_FULL 0x09 //!< Too many prepare writes have been queued +#define ATT_ERR_ATTR_NOT_FOUND 0x0a //!< No attribute found within the given attribute handle range +#define ATT_ERR_ATTR_NOT_LONG 0x0b //!< The attribute cannot be read using the Read Blob Request +#define ATT_ERR_INSUFFICIENT_KEY_SIZE 0x0c //!< The Encryption Key Size used for encrypting this link is insufficient +#define ATT_ERR_INVALID_VALUE_SIZE 0x0d //!< The attribute value length is invalid for the operation +#define ATT_ERR_UNLIKELY 0x0e //!< The attribute request that was requested has encountered an error that was very unlikely, and therefore could not be completed as requested +#define ATT_ERR_INSUFFICIENT_ENCRYPT 0x0f //!< The attribute requires encryption before it can be read or written +#define ATT_ERR_UNSUPPORTED_GRP_TYPE 0x10 //!< The attribute type is not a supported grouping attribute as defined by a higher layer specification +#define ATT_ERR_INSUFFICIENT_RESOURCES 0x11 //!< Insufficient Resources to complete the request +#define ATT_ERR_INVALID_VALUE 0x80 //!< The attribute value is invalid for the operation + +/********************************************************************* + * ATT Find By Type Value Response macros + */ +// Attribute Handle and Group End Handle pair indexes +#define ATT_ATTR_HANDLE_IDX( i ) ( (i) * (2 + 2) ) +#define ATT_GRP_END_HANDLE_IDX( i ) ( ATT_ATTR_HANDLE_IDX( (i) ) + 2 ) + +#define ATT_ATTR_HANDLE( info, i ) ( BUILD_UINT16( (info)[ATT_ATTR_HANDLE_IDX((i))], \ + (info)[ATT_ATTR_HANDLE_IDX((i))+1] ) ) +#define ATT_GRP_END_HANDLE( info, i ) ( BUILD_UINT16( (info)[ATT_GRP_END_HANDLE_IDX((i))], \ + (info)[ATT_GRP_END_HANDLE_IDX((i))+1] ) ) +/** @defgroup ATT_MSG_EVENT_DEFINES ATT Message Event IDs + * @{ + */ +#define ATT_FLOW_CTRL_VIOLATED_EVENT 0x7E //!< Sent when ATT flow control is violated on a connection. This event is sent as an TMOS message defined as attFlowCtrlViolatedEvt_t. +#define ATT_MTU_UPDATED_EVENT 0x7F //!< Sent when MTU is updated for a connection. This event is sent as an TMOS message defined as attMtuUpdatedEvt_t. +/** @} End ATT_MSG_EVENT_DEFINES */ + +/*** Opcode fields: bitmasks ***/ +// Size of 16-bit Bluetooth UUID +#define ATT_BT_UUID_SIZE 2 +// Size of 128-bit UUID +#define ATT_UUID_SIZE 16 +/******************************** GATT ***********************************/ + +// GATT Attribute Access Permissions Bit Fields +#define GATT_PERMIT_READ 0x01 //!< Attribute is Readable +#define GATT_PERMIT_WRITE 0x02 //!< Attribute is Writable +#define GATT_PERMIT_AUTHEN_READ 0x04 //!< Read requires Authentication +#define GATT_PERMIT_AUTHEN_WRITE 0x08 //!< Write requires Authentication +#define GATT_PERMIT_AUTHOR_READ 0x10 //!< Read requires Authorization +#define GATT_PERMIT_AUTHOR_WRITE 0x20 //!< Write requires Authorization +#define GATT_PERMIT_ENCRYPT_READ 0x40 //!< Read requires Encryption +#define GATT_PERMIT_ENCRYPT_WRITE 0x80 //!< Write requires Encryption + +// GATT Characteristic Properties Bit Fields +#define GATT_PROP_BCAST 0x01 //!< Permits broadcasts of the Characteristic Value +#define GATT_PROP_READ 0x02 //!< Permits reads of the Characteristic Value +#define GATT_PROP_WRITE_NO_RSP 0x04 //!< Permits writes of the Characteristic Value without response +#define GATT_PROP_WRITE 0x08 //!< Permits writes of the Characteristic Value with response +#define GATT_PROP_NOTIFY 0x10 //!< Permits notifications of a Characteristic Value without acknowledgement +#define GATT_PROP_INDICATE 0x20 //!< Permits indications of a Characteristic Value with acknowledgement +#define GATT_PROP_AUTHEN 0x40 //!< Permits signed writes to the Characteristic Value +#define GATT_PROP_EXTENDED 0x80 //!< Additional characteristic properties are defined in the Characteristic Extended Properties Descriptor + +// GATT local read or write operation +#define GATT_LOCAL_READ 0xFF +#define GATT_LOCAL_WRITE 0xFE + +// GATT Encryption Key Size Limits +#define GATT_MIN_ENCRYPT_KEY_SIZE 7 //!< GATT Minimum Encryption Key Size +#define GATT_MAX_ENCRYPT_KEY_SIZE 16 //!< GATT Maximum Encryption Key Size + +// Attribute handle definitions +#define GATT_INVALID_HANDLE 0x0000 // Invalid attribute handle +#define GATT_MIN_HANDLE 0x0001 // Minimum attribute handle +#define GATT_MAX_HANDLE 0xFFFF // Maximum attribute handle + +#define GATT_MAX_MTU 0xFFFF // Maximum MTU size + +// Attribute Access Permissions +#define gattPermitRead( a ) ( (a) & GATT_PERMIT_READ ) +#define gattPermitWrite( a ) ( (a) & GATT_PERMIT_WRITE ) +#define gattPermitAuthenRead( a ) ( (a) & GATT_PERMIT_AUTHEN_READ ) +#define gattPermitAuthenWrite( a ) ( (a) & GATT_PERMIT_AUTHEN_WRITE ) +#define gattPermitAuthorRead( a ) ( (a) & GATT_PERMIT_AUTHOR_READ ) +#define gattPermitAuthorWrite( a ) ( (a) & GATT_PERMIT_AUTHOR_WRITE ) +#define gattPermitEncryptRead( a ) ( (a) & GATT_PERMIT_ENCRYPT_READ ) +#define gattPermitEncryptWrite( a ) ( (a) & GATT_PERMIT_ENCRYPT_WRITE ) + +// Check for different UUID types +#define gattPrimaryServiceType( t ) ( ATT_CompareUUID( primaryServiceUUID, ATT_BT_UUID_SIZE, (t).uuid, (t).len ) ) +#define gattSecondaryServiceType( t ) ( ATT_CompareUUID( secondaryServiceUUID, ATT_BT_UUID_SIZE, (t).uuid, (t).len ) ) +#define gattCharacterType( t ) ( ATT_CompareUUID( characterUUID, ATT_BT_UUID_SIZE, (t).uuid, (t).len ) ) +#define gattIncludeType( t ) ( ATT_CompareUUID( includeUUID, ATT_BT_UUID_SIZE, (t).uuid, (t).len ) ) +#define gattServiceType( t ) ( gattPrimaryServiceType( (t) ) || gattSecondaryServiceType( (t) ) ) +#define GATT_MAX_NUM_CONN (4) + +// GATT Client Characteristic Configuration Bit Fields +#define GATT_CLIENT_CFG_NOTIFY 0x0001 //!< The Characteristic Value shall be notified +#define GATT_CLIENT_CFG_INDICATE 0x0002 //!< The Characteristic Value shall be indicated + +#define GATT_CFG_NO_OPERATION 0x0000 // No operation + +// All profile services bit fields +#define GATT_ALL_SERVICES 0xFFFFFFFF + +// The number of attribute records in a given attribute table +#define GATT_NUM_ATTRS( attrs ) ( sizeof( attrs ) / sizeof( gattAttribute_t ) ) + +// The handle of a service is the handle of the first attribute +#define GATT_SERVICE_HANDLE( attrs ) ( (attrs)[0].handle ) + +// The handle of the first included service (i = 1) is the value of the second attribute +#define GATT_INCLUDED_HANDLE( attrs, i ) ( *((uint16_t *)((attrs)[(i)].pValue)) ) + +// Client Characteristic Configuration table (from CCC attribute value pointer) +#define GATT_CCC_TBL( pValue ) ( (gattCharCfg_t *)(*((PTR_TYPE)(&pValue)))) + +/************************************ GAP *************************************/ +#define GAP_MSG_EVENT_DEFINES //!< GAP type of command +#define GAP_DEVICE_INIT_DONE_EVENT 0x00 //!< Sent when the Device Initialization is complete. This event is sent as an tmos message defined as gapDeviceInitDoneEvent_t. +#define GAP_DEVICE_DISCOVERY_EVENT 0x01 //!< Sent when the Device Discovery Process is complete. This event is sent as an tmos message defined as gapDevDiscEvent_t. +#define GAP_ADV_DATA_UPDATE_DONE_EVENT 0x02 //!< Sent when the Advertising Data or SCAN_RSP Data has been updated. This event is sent as an tmos message defined as gapAdvDataUpdateEvent_t. +#define GAP_MAKE_DISCOVERABLE_DONE_EVENT 0x03 //!< Sent when the Make Discoverable Request is complete. This event is sent as an tmos message defined as gapMakeDiscoverableRspEvent_t. +#define GAP_END_DISCOVERABLE_DONE_EVENT 0x04 //!< Sent when the Advertising has ended. This event is sent as an tmos message defined as gapEndDiscoverableRspEvent_t. +#define GAP_LINK_ESTABLISHED_EVENT 0x05 //!< Sent when the Establish Link Request is complete. This event is sent as an tmos message defined as gapEstLinkReqEvent_t. +#define GAP_LINK_TERMINATED_EVENT 0x06 //!< Sent when a connection was terminated. This event is sent as an tmos message defined as gapTerminateLinkEvent_t. +#define GAP_LINK_PARAM_UPDATE_EVENT 0x07 //!< Sent when an Update Parameters Event is received. This event is sent as an tmos message defined as gapLinkUpdateEvent_t. +#define GAP_RANDOM_ADDR_CHANGED_EVENT 0x08 //!< Sent when a random address was changed. This event is sent as an tmos message defined as gapRandomAddrEvent_t. +#define GAP_SIGNATURE_UPDATED_EVENT 0x09 //!< Sent when the device's signature counter is updated. This event is sent as an tmos message defined as gapSignUpdateEvent_t. +#define GAP_AUTHENTICATION_COMPLETE_EVENT 0x0A //!< Sent when the Authentication (pairing) process is complete. This event is sent as an tmos message defined as gapAuthCompleteEvent_t. +#define GAP_PASSKEY_NEEDED_EVENT 0x0B //!< Sent when a Passkey is needed. This is part of the pairing process. This event is sent as an tmos message defined as gapPasskeyNeededEvent_t. +#define GAP_SLAVE_REQUESTED_SECURITY_EVENT 0x0C //!< Sent when a Slave Security Request is received. This event is sent as an tmos message defined as gapSlaveSecurityReqEvent_t. +#define GAP_DEVICE_INFO_EVENT 0x0D //!< Sent during the Device Discovery Process when a device is discovered. This event is sent as an tmos message defined as gapDeviceInfoEvent_t. +#define GAP_BOND_COMPLETE_EVENT 0x0E //!< Sent when the bonding process is complete. This event is sent as an tmos message defined as gapBondCompleteEvent_t. +#define GAP_PAIRING_REQ_EVENT 0x0F //!< Sent when an unexpected Pairing Request is received. This event is sent as an tmos message defined as gapPairingReqEvent_t. +#define GAP_DIRECT_DEVICE_INFO_EVENT 0x10 //!< Sent when a direct Advertising Data is received. This event is sent as an tmos message defined as gapDirectDeviceInfoEvent_t. +#define GAP_PHY_UPDATE_EVENT 0x11 //!< Sent when a PHY Update Event is received. This event is sent as an tmos message defined as gapLinkUpdateEvent_t. +#define GAP_EXT_ADV_DEVICE_INFO_EVENT 0x12 //!< Sent when a Extended Advertising Data is received. This event is sent as an tmos message defined as gapExtAdvDeviceInfoEvent_t. +#define GAP_MAKE_PERIODIC_ADV_DONE_EVENT 0x13 //!< Sent when the Set Periodic Advertising enable is complete. This event is sent as an tmos message defined as gapMakePeriodicRspEvent_t. +#define GAP_END_PERIODIC_ADV_DONE_EVENT 0x14 //!< Sent when the Set Periodic Advertising disable is complete. This event is sent as an tmos message defined as gapEndPeriodicRspEvent_t. +#define GAP_SYNC_ESTABLISHED_EVENT 0x15 //!< Sent when a Periodic Advertising Sync Establish is complete. This event is sent as an tmos message defined as gapSyncEstablishedEvent_t. +#define GAP_PERIODIC_ADV_DEVICE_INFO_EVENT 0x16 //!< Sent when a Periodic Advertising Data is received. This event is sent as an tmos message defined as gapPeriodicAdvDeviceInfoEvent_t. +#define GAP_SYNC_LOST_EVENT 0x17 //!< Sent when a Periodic Advertising Sync was lost. This event is sent as an tmos message defined as gapSyncLostEvent_t. +#define GAP_SCAN_REQUEST_EVENT 0x19 //!< Sent when a SCAN_REQ PDU or an AUX_SCAN_REQ PDU has been received by the advertiser. This event is sent as an tmos message defined as gapScanReqReseiveEvent_t. +#define GAP_OOB_NEEDED_EVENT 0x1A //!< resv +#define GAP_MAKE_CONNECTIONESS_CTE_DONE_EVENT 0x1B //!< Sent when the Set Connectionless CTE Transmit enable is complete. This event is sent as an tmos message defined as gapMakeConnectionlessCTERspEvent_t. +#define GAP_END_CONNECTIONESS_CTE_DONE_EVENT 0x1C //!< Sent when the Set Connectionless CTE Transmit disable is complete. This event is sent as an tmos message defined as gapEndConnectionlessCTERspEvent_t. +#define GAP_PERI_ADV_SYNC_TRAN_RECEIVED_EVENT 0x1D //!< Sent when the periodic advertising sync transfer received. This event is sent as an tmos message defined as gapPeriTranReceivec_t. + +// GAP_PROFILE_ROLE_DEFINES GAP Profile Roles +#define GAP_PROFILE_BROADCASTER 0x01 //!< A device that sends advertising events only. +#define GAP_PROFILE_OBSERVER 0x02 //!< A device that receives advertising events only. +#define GAP_PROFILE_PERIPHERAL 0x04 //!< A device that accepts the establishment of an LE physical link using the connection establishment procedure +#define GAP_PROFILE_CENTRAL 0x08 //!< A device that supports the Central role initiates the establishment of a physical connection + +// GAP Status Return Values - returned as bStatus_t +#define bleGAPUserCanceled 0x30 //!< The user canceled the task +#define bleGAPConnNotAcceptable 0x31 //!< The connection was not accepted +#define bleGAPBondRejected 0x32 //!< The bond information was rejected. +#define bleGAPExpiredCanceled 0x33 //!< The duration has expired + +#define GAP_DEVICE_NAME_LEN 21 // Excluding null-terminate char + +// option defined +#define LISTEN_PERIODIC_ADVERTISING_MODE (1<<0) //!< used to determine whether the Periodic Advertiser List is used +#define REPORTING_INITIALLY_DISABLED (1<<1) //!< 0: Reporting initially enabled 1: Reporting initially disabled +#define DUPLICATE_FILTERING_INITIALLY_ENABLED (1<<2) //!< 0: Duplicate filtering initially disabled 1: Duplicate filtering initially enabled + +/*------------------------------------------------------------------- + * CONSTANTS + */ +/** @defgroup GAP_CONN_HANDLE_DEFINES GAP Special Connection Handles + * Used by GAP_TerminateLinkReq() + * @{ + */ +#define GAP_CONNHANDLE_INIT 0xFFFE //!< terminates a link create +#define GAP_CONNHANDLE_ALL 0xFFFF //!< terminates all links for the matching task ID. +/** @} End GAP_CONN_HANDLE_DEFINES */ + +// Privacy Flag States +#define GAP_PRIVACY_DISABLED 0x00 +#define GAP_PRIVACY_ENABLED 0x01 + +// GAP GATT Server Parameters used with GGS Get/Set Parameter and Application's Callback functions +#define GGS_DEVICE_NAME_ATT 0 // RW uint8_t[GAP_DEVICE_NAME_LEN] +#define GGS_APPEARANCE_ATT 1 // RW uint16_t +#define GGS_PERI_PRIVACY_FLAG_ATT 2 // RW uint8_t +#define GGS_RECONNCT_ADDR_ATT 3 // RW uint8_t[B_ADDR_LEN] +#define GGS_PERI_CONN_PARAM_ATT 4 // RW sizeof(gapPeriConnectParams_t) +#define GGS_PERI_PRIVACY_FLAG_PROPS 5 // RW uint8_t +#define GGS_W_PERMIT_DEVICE_NAME_ATT 6 // W uint8_t +#define GGS_W_PERMIT_APPEARANCE_ATT 7 // W uint8_t +#define GGS_W_PERMIT_PRIVACY_FLAG_ATT 8 // W uint8_t +#define GGS_CENT_ADDR_RES_ATT 9 // RW uint8_t +// GAP Services bit fields +#define GAP_SERVICE 0x00000001 + +// GAP_PARAMETER_ID_DEFINES GAP Parameter IDs +// Timers +#define TGAP_GEN_DISC_ADV_MIN 0 //!< Minimum time to remain advertising, when in Discoverable mode.Default 0-turns off the timeout. (n * 0.625 mSec). +#define TGAP_LIM_ADV_TIMEOUT 1 //!< Maximum time to remain advertising, when in Limited Discoverable mode.Default 180 seconds. (n * 1 seconds) +#define TGAP_DISC_SCAN 2 //!< Minimum time to perform scanning,Setting this parameter to 0 turns off the timeout.Default 10.24seconds. (n * 0.625 mSec) + +// when in General Discovery process +#define TGAP_DISC_ADV_INT_MIN 3 //!< Minimum advertising interval.Default 160. (n * 0.625 mSec) +#define TGAP_DISC_ADV_INT_MAX 4 //!< Maximum advertising interval.Default 160. (n * 0.625 mSec) +#define TGAP_DISC_SCAN_INT 5 //!< Scan interval used during Link Layer Scanning state.Default 16. (n * 0.625 mSec) +#define TGAP_DISC_SCAN_WIND 6 //!< Scan window used during Link Layer Scanning state.Default 16. (n * 0.625 mSec) + +// when in Connection Establishment process(1M PHY) +#define TGAP_CONN_EST_INT_MIN 7 //!< Minimum Link Layer connection interval.Default 80. (n * 1.25 mSec) +#define TGAP_CONN_EST_INT_MAX 8 //!< Maximum Link Layer connection interval.Default 80. (n * 1.25 mSec) +#define TGAP_CONN_EST_SCAN_INT 9 //!< Scan interval used during Link Layer Initiating state.Default 16. (n * 0.625 mSec) +#define TGAP_CONN_EST_SCAN_WIND 10 //!< Scan window used during Link Layer Initiating state.Default 16. (n * 0.625 mSec) +#define TGAP_CONN_EST_HIGH_SCAN_INT 11 //!< Scan interval used during Link Layer Initiating state, high duty scan cycle scan parameters (n * 0.625 mSec) +#define TGAP_CONN_EST_HIGH_SCAN_WIND 12 //!< Scan window used during Link Layer Initiating state, high duty scan cycle scan parameters (n * 0.625 mSec) +#define TGAP_CONN_EST_SUPERV_TIMEOUT 13 //!< Link Layer connection supervision timeout.Default 2000. (n * 10 mSec) +#define TGAP_CONN_EST_LATENCY 14 //!< Link Layer connection slave latency.Default 0. (in number of connection events) +#define TGAP_CONN_EST_MIN_CE_LEN 15 //!< Local informational parameter about minimum length of connection needed.Default 0. (n * 0.625 mSec) +#define TGAP_CONN_EST_MAX_CE_LEN 16 //!< Local informational parameter about maximum length of connection needed.Default 0. (n * 0.625 mSec) + +// Proprietary +#define TGAP_PRIVATE_ADDR_INT 17 //!< Minimum Time Interval between private (resolvable) address changes.Default 900. (n * 1 seconds) +#define TGAP_SM_TIMEOUT 18 //!< SM Message Timeout (milliseconds). Default 30 seconds. +#define TGAP_SM_MIN_KEY_LEN 19 //!< SM Minimum Key Length supported. Default 7. +#define TGAP_SM_MAX_KEY_LEN 20 //!< SM Maximum Key Length supported. Default 16. +#define TGAP_FILTER_ADV_REPORTS 21 //!< Filter duplicate advertising reports. Default TRUE. +#define TGAP_SCAN_RSSI_MIN 22 //!< Minimum RSSI required for scan advertising to be reported to the app. Default -127. +#define TGAP_REJECT_CONN_PARAMS 23 //!< Whether or not to reject Connection Parameter Update Request received on Central device. Default FALSE. +#define TGAP_AUTH_TASK_ID 24 //!< Task ID override for Task Authentication control (for stack internal use only) + +// v5.x +#define TGAP_ADV_TX_POWER 25 //!< Indicates the maximum power level Range: -127 �� N �� +126 Units: dBm.Default 127(Host has no preference). +#define TGAP_ADV_PRIMARY_PHY 26 //!< Indicates the PHY on which the advertising packets are transmitted on the primary advertising channel.LE 1M/LE Coded.Default GAP_PHY_VAL_LE_1M. +#define TGAP_ADV_SECONDARY_PHY 27 //!< LE 1M/LE 2M/LE Coded. Default GAP_PHY_VAL_LE_1M. +#define TGAP_ADV_SECONDARY_MAX_SKIP 28 //!< Maximum advertising events the Controller can skip before sending the AUX_ADV_IND packets on the secondary advertising channel. Default 0. +#define TGAP_ADV_ADVERTISING_SID 29 //!< Value of the Advertising SID subfield in the ADI field of the PDU Range:0-15. Default 0. +#define TGAP_ADV_SCAN_REQ_NOTIFY 30 //!< Scan request notifications enabled.Default 0-disabled. +#define TGAP_ADV_ADVERTISING_DURATION 31 //!< Advertising duration Range: 0x0001 - 0xFFFF Time = N * 10ms. Default 0-No advertising duration. +#define TGAP_ADV_MAX_EVENTS 32 //!< indicates the maximum number of extended advertising events.Range: 0x00 - 0xFF. Default 0(No maximum number of advertising events). + +// when in General Discovery process +#define TGAP_DISC_SCAN_PHY 33 //!< LE 1M/LE Coded. Default GAP_PHY_BIT_LE_1M. +#define TGAP_DISC_SCAN_CODED_INT 34 //!< Scan interval used during Link Layer coded Scanning state, when in General Discovery process (n * 0.625 mSec) +#define TGAP_DISC_SCAN_CODED_WIND 35 //!< Scan window used during Link Layer coded Scanning state, when in General Discovery process (n * 0.625 mSec) +#define TGAP_DISC_SCAN_DURATION 36 //!< Scan duration Range: 0x0001 - 0xFFFF Time = N * 10 ms. Default 0-Scan continuously until explicitly disable. +#define TGAP_DISC_SCAN_PERIOD 37 //!< resv. + +// when in Connection Establishment process(2M PHY) +#define TGAP_CONN_EST_INT_PHY 38 //!< LE 1M/LE Coded. Default GAP_PHY_BIT_LE_1M. +#define TGAP_CONN_EST_2M_INT_MIN 39 //!< Minimum Link Layer connection interval.Default 80. (n * 1.25 mSec) +#define TGAP_CONN_EST_2M_INT_MAX 40 //!< Maximum Link Layer connection interval.Default 80. (n * 1.25 mSec) +#define TGAP_CONN_EST_2M_SUPERV_TIMEOUT 41 //!< Link Layer connection supervision timeout.Default 2000. (n * 10 mSec) +#define TGAP_CONN_EST_2M_LATENCY 42 //!< Link Layer connection slave latency.Default 0. (in number of connection events) +#define TGAP_CONN_EST_2M_MIN_CE_LEN 43 //!< Local informational parameter about minimum length of connection needed.Default 0. (n * 0.625 mSec) +#define TGAP_CONN_EST_2M_MAX_CE_LEN 44 //!< Local informational parameter about maximum length of connection needed.Default 0. (n * 0.625 mSec) + +// when in Connection Establishment process(Coded PHY) +#define TGAP_CONN_EST_CODED_INT_MIN 45 //!< Minimum Link Layer connection interval.Default 80. (n * 1.25 mSec) +#define TGAP_CONN_EST_CODED_INT_MAX 46 //!< Maximum Link Layer connection interval.Default 80. (n * 1.25 mSec) +#define TGAP_CONN_EST_CODED_SCAN_INT 47 //!< Scan interval used during Link Layer Initiating state.Default 16. (n * 0.625 mSec) +#define TGAP_CONN_EST_CODED_SCAN_WIND 48 //!< Scan window used during Link Layer Initiating state.Default 16. (n * 0.625 mSec) +#define TGAP_CONN_EST_CODED_HIGH_SCAN_INT 49 //!< Scan interval used during Link Layer Initiating state, high duty scan cycle scan parameters (n * 0.625 mSec) +#define TGAP_CONN_EST_CODED_HIGH_SCAN_WIND 50 //!< Scan window used during Link Layer Initiating state, high duty scan cycle scan parameters (n * 0.625 mSec) +#define TGAP_CONN_EST_CODED_SUPERV_TIMEOUT 51 //!< Link Layer connection supervision timeout.Default 2000. (n * 10 mSec) +#define TGAP_CONN_EST_CODED_LATENCY 52 //!< Link Layer connection slave latency.Default 0. (in number of connection events) +#define TGAP_CONN_EST_CODED_MIN_CE_LEN 53 //!< Local informational parameter about minimum length of connection needed.Default 0. (n * 0.625 mSec) +#define TGAP_CONN_EST_CODED_MAX_CE_LEN 54 //!< Local informational parameter about maximum length of connection needed.Default 0. (n * 0.625 mSec) + +// periodic advertising +#define TGAP_PERIODIC_ADV_INT_MIN 55 //!< Minimum periodic advertising interval.Range: 0x0006 to 0xFFFF.Default 160. (n * 1.25 mSec) +#define TGAP_PERIODIC_ADV_INT_MAX 56 //!< Maximum periodic advertising interval.Range: 0x0006 to 0xFFFF.Default 160. (n * 1.25 mSec) +#define TGAP_PERIODIC_ADV_PROPERTIES 57 //!< Include TxPower in the periodic advertising PDU. + +#define TGAP_SCAN_MAX_LENGTH 58 //!< Extended scan maximum data length.Default 460 +#define TGAP_AFH_CHANNEL_MDOE 59 //!< whether t he Controller's channel assessment scheme is enabled or disabled.Default disabled. + +#define TGAP_PARAMID_MAX 60 //!< ID MAX-valid Parameter ID + +// GAP_DEVDISC_MODE_DEFINES GAP Device Discovery Modes +#define DEVDISC_MODE_NONDISCOVERABLE 0x00 //!< No discoverable setting +#define DEVDISC_MODE_GENERAL 0x01 //!< General Discoverable devices +#define DEVDISC_MODE_LIMITED 0x02 //!< Limited Discoverable devices +#define DEVDISC_MODE_ALL 0x03 //!< Not filtered + +// GAP_ADDR_TYPE_DEFINES GAP Address Types +#define ADDRTYPE_PUBLIC 0x00 //!< Use the BD_ADDR +#define ADDRTYPE_STATIC 0x01 //!< Static address +#define ADDRTYPE_PRIVATE_NONRESOLVE 0x02 //!< Generate Non-Resolvable Private Address +#define ADDRTYPE_PRIVATE_RESOLVE 0x03 //!< Generate Resolvable Private Address + +// GAP_ADVERTISEMENT_TYPE_DEFINES GAP Advertising Event Types +#define GAP_ADTYPE_ADV_IND 0x00 //!< Connectable undirected event typet +#define GAP_ADTYPE_ADV_HDC_DIRECT_IND 0x01 //!< Connectable high duty cycle directed event type +#define GAP_ADTYPE_ADV_SCAN_IND 0x02 //!< Scannable undirected event type +#define GAP_ADTYPE_ADV_NONCONN_IND 0x03 //!< Non-Connectable undirected event type +#define GAP_ADTYPE_ADV_LDC_DIRECT_IND 0x04 //!< Connectable low duty cycle directed event type +//v5.x +#define GAP_ADTYPE_EXT_CONN_DIRECT 0x05 //!< extend Connectable directed event type +#define GAP_ADTYPE_EXT_SCAN_UNDIRECT 0x06 //!< extend Scannable undirected event type +#define GAP_ADTYPE_EXT_NONCONN_NONSCAN_UNDIRECT 0x07 //!< extend Non-Connectable and Non-Scannable undirected event type +#define GAP_ADTYPE_EXT_CONN_UNDIRECT 0x08 //!< extend Connectable undirected event type +#define GAP_ADTYPE_EXT_SCAN_DIRECT 0x09 //!< extend Scannable directed event type +#define GAP_ADTYPE_EXT_NONCONN_NONSCAN_DIRECT 0x0A //!< extend Non-Connectable and Non-Scannable directed event type + +// GAP_ADVERTISEMENT_TYPE_DEFINES GAP Advertising PHY VAL TYPE(GAP_PHY_VAL_TYPE) +#define GAP_PHY_VAL_TYPE +#define GAP_PHY_VAL_LE_1M 0x01 +#define GAP_PHY_VAL_LE_2M 0x02 +#define GAP_PHY_VAL_LE_CODED 0x03 + +// GAP_ADVERTISEMENT_TYPE_DEFINES GAP Scan PHY VAL TYPE(GAP_PHY_BIT_TYPE) +#define GAP_PHY_BIT_TYPE +#define GAP_PHY_BIT_LE_1M (1<<0) +#define GAP_PHY_BIT_LE_2M (1<<1) +#define GAP_PHY_BIT_LE_CODED (1<<2) +#define GAP_PHY_BIT_ALL (GAP_PHY_BIT_LE_1M|GAP_PHY_BIT_LE_2M|GAP_PHY_BIT_LE_CODED) +#define GAP_PHY_BIT_LE_CODED_S2 (1<<3) + +// PHY_OPTIONS preferred coding when transmitting on the LE Coded PHY +#define GAP_PHY_OPTIONS_TYPE +#define GAP_PHY_OPTIONS_NOPRE 0x00 // 0:no preferred +#define GAP_PHY_OPTIONS_S2 0x01 +#define GAP_PHY_OPTIONS_S8 0x02 + +// GAP_ADVERTISEMENT_TYPE_DEFINES GAP Periodic Advertising Properties +#define GAP_PERI_PROPERTIES_INCLUDE_TXPOWER (1<<6) + +// GAP Advertising Report Event Types +#define GAP_ADVERTISEMENT_REPORT_TYPE_DEFINES +// bit0 to 4 ADVERTISEMENT_TYPE:defined for gapExtAdvDeviceInfoEvent_t Advertisement data type +#define GAP_ADRPT_ADV_IND 0x00 //!< Connectable undirected advertisement +#define GAP_ADRPT_ADV_DIRECT_IND 0x01 //!< Connectable directed advertisement +#define GAP_ADRPT_ADV_SCAN_IND 0x02 //!< Scannable undirected advertisement +#define GAP_ADRPT_ADV_NONCONN_IND 0x03 //!< Non-Connectable undirected advertisement +#define GAP_ADRPT_SCAN_RSP 0x04 //!< Scan Response +#define GAP_ADRPT_EXT_CONN_DIRECT 0x05 //!< extend Connectable directed report type +#define GAP_ADRPT_EXT_SCAN_UNDIRECT 0x06 //!< extend Scannable undirected report type +#define GAP_ADRPT_EXT_NONCONN_NONSCAN_UNDIRECT 0x07 //!< extend Non-Connectable and Non-Scannable undirected report type +#define GAP_ADRPT_EXT_CONN_UNDIRECT 0x08 //!< extend Connectable undirected report type +#define GAP_ADRPT_EXT_SCAN_DIRECT 0x09 //!< extend Scannable directed report type +#define GAP_ADRPT_EXT_NONCONN_NONSCAN_DIRECT 0x0A //!< extend Non-Connectable and Non-Scannable directed report type +#define GAP_ADRPT_EXT_SCAN_RESPONSE 0x0B //!< extend Scan Response report type +// bit5 to 6 Data status:defined for gapExtAdvDeviceInfoEvent_t Advertisement data type +#define GAP_ADRPT_EXT_DATA_MASK (3<<5) +#define GAP_ADRPT_EXT_DATA_COMPLETE (0<<5) //!< Complete +#define GAP_ADRPT_EXT_DATA_INCOMPLETE (1<<5) //!< more data to come +#define GAP_ADRPT_EXT_DATA_LAST (2<<5) //!< Incomplete, data truncated, no more to come + +// GAP_EXTEND_ADVERTISEMENT_REPORT_TYPE_DEFINES GAP Extend Advertising Report Event Types +#define GAP_ADRPT_ADV_CONNECTABLE (1<<0) +#define GAP_ADRPT_ADV_SCANNABLE (1<<1) +#define GAP_ADRPT_ADV_DITECTED (1<<2) +#define GAP_ADRPT_SCAN_RESPONSE (1<<3) + +// GAP_FILTER_POLICY_DEFINES GAP Advertiser Filter Scan Parameters +#define GAP_FILTER_POLICY_ALL 0x00 //!< Allow Scan Request from Any, Allow Connect Request from Any (default). +#define GAP_FILTER_POLICY_WHITE_SCAN 0x01 //!< Allow Scan Request from White List Only, Allow Connect from Any +#define GAP_FILTER_POLICY_WHITE_CON 0x02 //!< Allow Scan Request from Any, Connect from White List Only +#define GAP_FILTER_POLICY_WHITE 0x03 //!< Allow Scan Request and Connect from White List Only + +//! Maximum Pairing Passcode/Passkey value. Range of a passkey can be 0 - 999,999. +#define GAP_PASSCODE_MAX 999999 + +/** Sign Counter Initialized - Sign counter hasn't been used yet. Used when setting up + * a connection's signing information. + */ +#define GAP_INIT_SIGN_COUNTER 0xFFFFFFFF + +// GAP_ADVCHAN_DEFINES GAP Advertisement Channel Map +#define GAP_ADVCHAN_37 0x01 //!< Advertisement Channel 37 +#define GAP_ADVCHAN_38 0x02 //!< Advertisement Channel 38 +#define GAP_ADVCHAN_39 0x04 //!< Advertisement Channel 39 +#define GAP_ADVCHAN_ALL (GAP_ADVCHAN_37 | GAP_ADVCHAN_38 | GAP_ADVCHAN_39) //!< All Advertisement Channels Enabled + +// GAP_ADTYPE_DEFINES GAP Advertisement Data Types +#define GAP_ADTYPE_FLAGS 0x01 //!< Discovery Mode: @ref GAP_ADTYPE_FLAGS_MODES +#define GAP_ADTYPE_16BIT_MORE 0x02 //!< Service: More 16-bit UUIDs available +#define GAP_ADTYPE_16BIT_COMPLETE 0x03 //!< Service: Complete list of 16-bit UUIDs +#define GAP_ADTYPE_32BIT_MORE 0x04 //!< Service: More 32-bit UUIDs available +#define GAP_ADTYPE_32BIT_COMPLETE 0x05 //!< Service: Complete list of 32-bit UUIDs +#define GAP_ADTYPE_128BIT_MORE 0x06 //!< Service: More 128-bit UUIDs available +#define GAP_ADTYPE_128BIT_COMPLETE 0x07 //!< Service: Complete list of 128-bit UUIDs +#define GAP_ADTYPE_LOCAL_NAME_SHORT 0x08 //!< Shortened local name +#define GAP_ADTYPE_LOCAL_NAME_COMPLETE 0x09 //!< Complete local name +#define GAP_ADTYPE_POWER_LEVEL 0x0A //!< TX Power Level: -127 to +127 dBm +#define GAP_ADTYPE_OOB_CLASS_OF_DEVICE 0x0D //!< Simple Pairing OOB Tag: Class of device (3 octets) +#define GAP_ADTYPE_OOB_SIMPLE_PAIRING_HASHC 0x0E //!< Simple Pairing OOB Tag: Simple Pairing Hash C (16 octets) +#define GAP_ADTYPE_OOB_SIMPLE_PAIRING_RANDR 0x0F //!< Simple Pairing OOB Tag: Simple Pairing Randomizer R (16 octets) +#define GAP_ADTYPE_SM_TK 0x10 //!< Security Manager TK Value +#define GAP_ADTYPE_SM_OOB_FLAG 0x11 //!< Security Manager OOB Flags +#define GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE 0x12 //!< Min and Max values of the connection interval (2 octets Min, 2 octets Max) (0xFFFF indicates no conn interval min or max) +#define GAP_ADTYPE_SIGNED_DATA 0x13 //!< Signed Data field +#define GAP_ADTYPE_SERVICES_LIST_16BIT 0x14 //!< Service Solicitation: list of 16-bit Service UUIDs +#define GAP_ADTYPE_SERVICES_LIST_128BIT 0x15 //!< Service Solicitation: list of 128-bit Service UUIDs +#define GAP_ADTYPE_SERVICE_DATA 0x16 //!< Service Data - 16-bit UUID +#define GAP_ADTYPE_PUBLIC_TARGET_ADDR 0x17 //!< Public Target Address +#define GAP_ADTYPE_RANDOM_TARGET_ADDR 0x18 //!< Random Target Address +#define GAP_ADTYPE_APPEARANCE 0x19 //!< Appearance +#define GAP_ADTYPE_ADV_INTERVAL 0x1A //!< Advertising Interval +#define GAP_ADTYPE_LE_BD_ADDR 0x1B //!< LE Bluetooth Device Address +#define GAP_ADTYPE_LE_ROLE 0x1C //!< LE Role +#define GAP_ADTYPE_SIMPLE_PAIRING_HASHC_256 0x1D //!< Simple Pairing Hash C-256 +#define GAP_ADTYPE_SIMPLE_PAIRING_RANDR_256 0x1E //!< Simple Pairing Randomizer R-256 +#define GAP_ADTYPE_SERVICE_DATA_32BIT 0x20 //!< Service Data - 32-bit UUID +#define GAP_ADTYPE_SERVICE_DATA_128BIT 0x21 //!< Service Data - 128-bit UUID +#define GAP_ADTYPE_URI 0x24 //!< URI +#define GAP_ADTYPE_INDOOR_POSITION 0x25 //!< Indoor Positioning Service v1.0 or later +#define GAP_ADTYPE_TRAN_DISCOVERY_DATA 0x26 //!< Transport Discovery Service v1.0 or later +#define GAP_ADTYPE_SUPPORTED_FEATURES 0x27 //!< LE Supported Features +#define GAP_ADTYPE_CHANNEL_MAP_UPDATE 0x28 //!< Channel Map Update Indication +#define GAP_ADTYPE_PB_ADV 0x29 //!< PB-ADV. Mesh Profile Specification Section 5.2.1 +#define GAP_ADTYPE_MESH_MESSAGE 0x2A //!< Mesh Message. Mesh Profile Specification Section 3.3.1 +#define GAP_ADTYPE_MESH_BEACON 0x2B //!< Mesh Beacon. Mesh Profile Specification Section 3.9 +#define GAP_ADTYPE_BIG_INFO 0x2C //!< BIGInfo +#define GAP_ADTYPE_BROADCAST_CODE 0x2D //!< Broadcast_Code +#define GAP_ADTYPE_RSL_SET_IDENT 0x2E //!< Resolvable Set Identifier.Coordinated Set Identification Profile 1.0 +#define GAP_ADTYPE_ADV_INTERVAL_LONG 0x2F //!< Advertising Interval - long +#define GAP_ADTYPE_3D_INFO_DATA 0x3D //!< 3D Information Data +#define GAP_ADTYPE_MANUFACTURER_SPECIFIC 0xFF //!< Manufacturer Specific Data: first 2 octets contain the Company Identifier Code followed by the additional manufacturer specific data + +// GAP_ADTYPE_FLAGS_MODES GAP ADTYPE Flags Discovery Modes +#define GAP_ADTYPE_FLAGS_LIMITED 0x01 //!< Discovery Mode: LE Limited Discoverable Mode +#define GAP_ADTYPE_FLAGS_GENERAL 0x02 //!< Discovery Mode: LE General Discoverable Mode +#define GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED 0x04 //!< Discovery Mode: BR/EDR Not Supported + +// GAP_APPEARANCE_VALUES GAP Appearance Values +#define GAP_APPEARE_UNKNOWN 0x0000 //!< Unknown +#define GAP_APPEARE_GENERIC_PHONE 0x0040 //!< Generic Phone +#define GAP_APPEARE_GENERIC_COMPUTER 0x0080 //!< Generic Computer +#define GAP_APPEARE_GENERIC_WATCH 0x00C0 //!< Generic Watch +#define GAP_APPEARE_WATCH_SPORTS 0x00C1 //!< Watch: Sports Watch +#define GAP_APPEARE_GENERIC_CLOCK 0x0100 //!< Generic Clock +#define GAP_APPEARE_GENERIC_DISPLAY 0x0140 //!< Generic Display +#define GAP_APPEARE_GENERIC_RC 0x0180 //!< Generic Remote Control +#define GAP_APPEARE_GENERIC_EYE_GALSSES 0x01C0 //!< Generic Eye-glasses +#define GAP_APPEARE_GENERIC_TAG 0x0200 //!< Generic Tag +#define GAP_APPEARE_GENERIC_KEYRING 0x0240 //!< Generic Keyring +#define GAP_APPEARE_GENERIC_MEDIA_PLAYER 0x0280 //!< Generic Media Player +#define GAP_APPEARE_GENERIC_BARCODE_SCANNER 0x02C0 //!< Generic Barcode Scanner +#define GAP_APPEARE_GENERIC_THERMOMETER 0x0300 //!< Generic Thermometer +#define GAP_APPEARE_GENERIC_THERMO_EAR 0x0301 //!< Thermometer: Ear +#define GAP_APPEARE_GENERIC_HR_SENSOR 0x0340 //!< Generic Heart rate Sensor +#define GAP_APPEARE_GENERIC_HRS_BELT 0x0341 //!< Heart Rate Sensor: Heart Rate Belt +#define GAP_APPEARE_GENERIC_BLOOD_PRESSURE 0x0380 //!< Generic Blood Pressure +#define GAP_APPEARE_GENERIC_BP_ARM 0x0381 //!< Blood Pressure: Arm +#define GAP_APPEARE_GENERIC_BP_WRIST 0x0382 //!< Blood Pressure: Wrist +#define GAP_APPEARE_GENERIC_HID 0x03C0 //!< Generic Human Interface Device (HID) +#define GAP_APPEARE_HID_KEYBOARD 0x03C1 //!< HID Keyboard +#define GAP_APPEARE_HID_MOUSE 0x03C2 //!< HID Mouse +#define GAP_APPEARE_HID_JOYSTIC 0x03C3 //!< HID Joystick +#define GAP_APPEARE_HID_GAMEPAD 0x03C4 //!< HID Gamepad +#define GAP_APPEARE_HID_DIGITIZER_TYABLET 0x03C5 //!< HID Digitizer Tablet +#define GAP_APPEARE_HID_DIGITAL_CARDREADER 0x03C6 //!< HID Card Reader +#define GAP_APPEARE_HID_DIGITAL_PEN 0x03C7 //!< HID Digital Pen +#define GAP_APPEARE_HID_BARCODE_SCANNER 0x03C8 //!< HID Barcode Scanner + +/************************************gapRole***********************************/ +// GAPROLE_PROFILE_PARAMETERS GAP Role Manager Parameters +#define GAPROLE_PROFILEROLE 0x300 //!< Reading this parameter will return GAP Role type. Read Only. Size is uint8_t. +#define GAPROLE_IRK 0x301 //!< Identity Resolving Key. Read/Write. Size is uint8_t[KEYLEN]. Default is all 0, which means that the IRK will be randomly generated. +#define GAPROLE_SRK 0x302 //!< Signature Resolving Key. Read/Write. Size is uint8_t[KEYLEN]. Default is all 0, which means that the SRK will be randomly generated. +#define GAPROLE_SIGNCOUNTER 0x303 //!< Sign Counter. Read/Write. Size is uint32_t. Default is 0. +#define GAPROLE_BD_ADDR 0x304 //!< Device's Address. Read Only. Size is uint8_t[B_ADDR_LEN]. This item is read from the controller. +#define GAPROLE_ADVERT_ENABLED 0x305 //!< Enable/Disable Advertising. Read/Write. Size is uint8_t. Default is TRUE=Enabled. +#define GAPROLE_ADVERT_DATA 0x306 //!< Advertisement Data. Read/Write. Max size is B_MAX_ADV_EXT_LEN. Default to all 0. +#define GAPROLE_SCAN_RSP_DATA 0x307 //!< Scan Response Data. Read/Write. Max size is B_MAX_ADV_EXT_LEN. Defaults to all 0. +#define GAPROLE_ADV_EVENT_TYPE 0x308 //!< Advertisement Type. Read/Write. Size is uint8_t. Default is GAP_ADTYPE_ADV_IND. +#define GAPROLE_ADV_DIRECT_TYPE 0x309 //!< Direct Advertisement Address Type. Read/Write. Size is uint8_t. Default is ADDRTYPE_PUBLIC. +#define GAPROLE_ADV_DIRECT_ADDR 0x30A //!< Direct Advertisement Address. Read/Write. Size is uint8_t[B_ADDR_LEN]. Default is NULL. +#define GAPROLE_ADV_CHANNEL_MAP 0x30B //!< Which channels to advertise on. Read/Write Size is uint8_t. Default is GAP_ADVCHAN_ALL +#define GAPROLE_ADV_FILTER_POLICY 0x30C //!< Filter Policy. Ignored when directed advertising is used. Read/Write. Size is uint8_t. Default is GAP_FILTER_POLICY_ALL. +#define GAPROLE_STATE 0x30D //!< Reading this parameter will return GAP Peripheral Role State. Read Only. Size is uint8_t. +#define GAPROLE_MAX_SCAN_RES 0x30E //!< Maximum number of discover scan results to receive. Default is 0 = unlimited. +#define GAPROLE_MIN_CONN_INTERVAL 0x311 //!< Minimum Connection Interval to allow (n * 1.25ms). Range: 7.5 msec to 4 seconds (0x0006 to 0x0C80). Read/Write. Size is uint16_t. Default is 7.5 milliseconds (0x0006). +#define GAPROLE_MAX_CONN_INTERVAL 0x312 //!< Maximum Connection Interval to allow (n * 1.25ms). Range: 7.5 msec to 4 seconds (0x0006 to 0x0C80). Read/Write. Size is uint16_t. Default is 4 seconds (0x0C80). +// v5.x +#define GAPROLE_PHY_TX_SUPPORTED 0x313 //!< The transmitter PHYs that the Host prefers the Controller to use.Default is GAP_PHY_BIT_ALL +#define GAPROLE_PHY_RX_SUPPORTED 0x314 //!< The receiver PHYs that the Host prefers the Controller to use.Default is GAP_PHY_BIT_ALL +#define GAPROLE_PERIODIC_ADVERT_DATA 0x315 //!< Periodic advertisement Data. Read/Write. Max size is B_MAX_ADV_PERIODIC_LEN. Default to all 0. +#define GAPROLE_PERIODIC_ADVERT_ENABLED 0x316 //!< bit0:Enable/Disable Periodic Advertising. Read/Write. Size is uint8_t. Default is FALSE=Disable. + //!< bit1:Include the ADI field in AUX_SYNC_IND PDUs + +/************************************GAPBOND***********************************/ +// GAPBOND_PROFILE_PARAMETERS GAP Bond Manager Parameters +#define GAPBOND_PERI_PAIRING_MODE 0x400 //!< Pairing Mode: @ref GAPBOND_PAIRING_MODE_DEFINES. Read/Write. Size is uint8_t. Default is GAPBOND_PAIRING_MODE_WAIT_FOR_REQ. +#define GAPBOND_PERI_MITM_PROTECTION 0x401 //!< Man-In-The-Middle (MITM) basically turns on Passkey protection in the pairing algorithm. Read/Write. Size is uint8_t. Default is 0(disabled). +#define GAPBOND_PERI_IO_CAPABILITIES 0x402 //!< I/O capabilities. Read/Write. Size is uint8_t. Default is GAPBOND_IO_CAP_DISPLAY_ONLY @ref GAPBOND_IO_CAP_DEFINES. +#define GAPBOND_PERI_OOB_ENABLED 0x403 //!< OOB data available for pairing algorithm. Read/Write. Size is uint8_t. Default is 0(disabled). +#define GAPBOND_PERI_OOB_DATA 0x404 //!< OOB Data. Read/Write. size uint8_t[16]. Default is all 0's. +#define GAPBOND_PERI_BONDING_ENABLED 0x405 //!< Request Bonding during the pairing process if enabled. Read/Write. Size is uint8_t. Default is 0(disabled). +#define GAPBOND_PERI_KEY_DIST_LIST 0x406 //!< The key distribution list for bonding. size is uint8_t. @ref GAPBOND_KEY_DIST_DEFINES. Default is 0x77. +#define GAPBOND_PERI_DEFAULT_PASSCODE 0x407 //!< The default passcode for MITM protection. size is uint32_t. Range is 0 - 999,999. Default is 0. +#define GAPBOND_CENT_PAIRING_MODE 0x408 //!< Pairing Mode: @ref GAPBOND_PAIRING_MODE_DEFINES. Read/Write. Size is uint8_t. Default is GAPBOND_PAIRING_MODE_WAIT_FOR_REQ. +#define GAPBOND_CENT_MITM_PROTECTION 0x409 //!< Man-In-The-Middle (MITM) basically turns on Passkey protection in the pairing algorithm. Read/Write. Size is uint8_t. Default is 0(disabled). +#define GAPBOND_CENT_IO_CAPABILITIES 0x40A //!< I/O capabilities. Read/Write. Size is uint8_t. Default is GAPBOND_IO_CAP_DISPLAY_ONLY @ref GAPBOND_IO_CAP_DEFINES. +#define GAPBOND_CENT_OOB_ENABLED 0x40B //!< OOB data available for pairing algorithm. Read/Write. Size is uint8_t. Default is 0(disabled). +#define GAPBOND_CENT_OOB_DATA 0x40C //!< OOB Data. Read/Write. size uint8_t[16]. Default is all 0's. +#define GAPBOND_CENT_BONDING_ENABLED 0x40D //!< Request Bonding during the pairing process if enabled. Read/Write. Size is uint8_t. Default is 0(disabled). +#define GAPBOND_CENT_KEY_DIST_LIST 0x40E //!< The key distribution list for bonding. size is uint8_t. @ref GAPBOND_KEY_DIST_DEFINES. Default is 0x77. +#define GAPBOND_CENT_DEFAULT_PASSCODE 0x40F //!< The default passcode for MITM protection. size is uint32_t. Range is 0 - 999,999. Default is 0. +#define GAPBOND_ERASE_ALLBONDS 0x410 //!< Erase all of the bonded devices. Write Only. No Size. +#define GAPBOND_AUTO_FAIL_PAIRING 0x411 //!< TEST MODE (DO NOT USE) to automatically send a Pairing Fail when a Pairing Request is received. Read/Write. size is uint8_t. Default is 0 (disabled). +#define GAPBOND_AUTO_FAIL_REASON 0x412 //!< TEST MODE (DO NOT USE) Pairing Fail reason when auto failing. Read/Write. size is uint8_t. Default is 0x05 (SMP_PAIRING_FAILED_NOT_SUPPORTED). +#define GAPBOND_KEYSIZE 0x413 //!< Key Size used in pairing. Read/Write. size is uint8_t. Default is 16. +#define GAPBOND_AUTO_SYNC_WL 0x414 //!< Clears the White List adds to it each unique address stored by bonds in NV. Read/Write. Size is uint8_t. Default is FALSE. +#define GAPBOND_BOND_COUNT 0x415 //!< Gets the total number of bonds stored in NV. Read Only. Size is uint8_t. Default is 0 (no bonds). +#define GAPBOND_BOND_FAIL_ACTION 0x416 //!< Possible actions Central may take upon an unsuccessful bonding. Write Only. Size is uint8_t. Default is 0x02 (Terminate link upon unsuccessful bonding). +#define GAPBOND_ERASE_SINGLEBOND 0x417 //!< Erase a single bonded device. Write only. Must provide address type followed by device address. +#define GAPBOND_BOND_AUTO 0x418 //!< Auto save bonds into FLASH. Write Only. size is uint8_t. Default is 1(enabled). +#define GAPBOND_BOND_UPDATE 0x419 //!< Save current bonds into FLASH. Write Only. No Size. +#define GAPBOND_DISABLE_SINGLEBOND 0x41A //!< Disable a single bonded device. Write only. Must provide address type followed by device address. +#define GAPBOND_ENABLE_SINGLEBOND 0x41B //!< Ensable a single bonded device. Write only. Must provide address type followed by device address. +#define GAPBOND_DISABLE_ALLBONDS 0x41C //!< Disable all of the bonded devices. Write Only. No Size. +#define GAPBOND_ENABLE_ALLBONDS 0x41D //!< Ensable all of the bonded devices. Write Only. No Size. +#define GAPBOND_ERASE_AUTO 0x41E //!< Auto erase all of the bonded devices when the maximum number is reached.Size is uint8_t. Default is 1(enabled). +#define GAPBOND_AUTO_SYNC_RL 0x41F //!< Clears the Resolving List adds to it each unique address stored by bonds in NV. Read/Write. Size is uint8_t. Default is FALSE. +#define GAPBOND_SET_ENC_PARAMS 0x420 //!< Set bonding parameters.size is bondEncParams_t. +#define GAPBOND_PERI_SC_PROTECTION 0x421 //!< Set peripheral sc enable. Default is FALSE. +#define GAPBOND_CENT_SC_PROTECTION 0x422 //!< Set central sc enable. Default is FALSE. + +// GAPBOND_PAIRING_MODE_DEFINES GAP Bond Manager Pairing Modes +#define GAPBOND_PAIRING_MODE_NO_PAIRING 0x00 //!< Pairing is not allowed +#define GAPBOND_PAIRING_MODE_WAIT_FOR_REQ 0x01 //!< Wait for a pairing request or slave security request +#define GAPBOND_PAIRING_MODE_INITIATE 0x02 //!< Don't wait, initiate a pairing request or slave security request + +// GAPBOND_IO_CAP_DEFINES GAP Bond Manager I/O Capabilities +#define GAPBOND_IO_CAP_DISPLAY_ONLY 0x00 //!< Display Only Device +#define GAPBOND_IO_CAP_DISPLAY_YES_NO 0x01 //!< Display and Yes and No Capable +#define GAPBOND_IO_CAP_KEYBOARD_ONLY 0x02 //!< Keyboard Only +#define GAPBOND_IO_CAP_NO_INPUT_NO_OUTPUT 0x03 //!< No Display or Input Device +#define GAPBOND_IO_CAP_KEYBOARD_DISPLAY 0x04 //!< Both Keyboard and Display Capable + +// GAPBOND_KEY_DIST_DEFINES GAP Bond Manager Key Distribution +#define GAPBOND_KEYDIST_SENCKEY 0x01 //!< Slave Encryption Key +#define GAPBOND_KEYDIST_SIDKEY 0x02 //!< Slave IRK and ID information +#define GAPBOND_KEYDIST_SSIGN 0x04 //!< Slave CSRK +#define GAPBOND_KEYDIST_SLINK 0x08 //!< Slave Link Key +#define GAPBOND_KEYDIST_MENCKEY 0x10 //!< Master Encrypton Key +#define GAPBOND_KEYDIST_MIDKEY 0x20 //!< Master IRK and ID information +#define GAPBOND_KEYDIST_MSIGN 0x40 //!< Master CSRK +#define GAPBOND_KEYDIST_MLINK 0x80 //!< Master Link Key + +// GAPBOND_PAIRING_STATE_DEFINES GAP Bond Manager Pairing States +#define GAPBOND_PAIRING_STATE_STARTED 0x00 //!< Pairing started +#define GAPBOND_PAIRING_STATE_COMPLETE 0x01 //!< Pairing complete +#define GAPBOND_PAIRING_STATE_BONDED 0x02 //!< Devices bonded +#define GAPBOND_PAIRING_STATE_BOND_SAVED 0x03 //!< Bonding record saved in NV + +// SMP_PAIRING_FAILED_DEFINES Pairing failure status values +#define SMP_PAIRING_FAILED_PASSKEY_ENTRY_FAILED 0x01 //!< The user input of the passkey failed, for example, the user cancelled the operation. +#define SMP_PAIRING_FAILED_OOB_NOT_AVAIL 0x02 //!< The OOB data is not available +#define SMP_PAIRING_FAILED_AUTH_REQ 0x03 //!< The pairing procedure can't be performed as authentication requirements can't be met due to IO capabilities of one or both devices +#define SMP_PAIRING_FAILED_CONFIRM_VALUE 0x04 //!< The confirm value doesn't match the calculated compare value +#define SMP_PAIRING_FAILED_NOT_SUPPORTED 0x05 //!< Pairing isn't supported by the device +#define SMP_PAIRING_FAILED_ENC_KEY_SIZE 0x06 //!< The resultant encryption key size is insufficient for the security requirements of this device. +#define SMP_PAIRING_FAILED_CMD_NOT_SUPPORTED 0x07 //!< The SMP command received is not supported on this device. +#define SMP_PAIRING_FAILED_UNSPECIFIED 0x08 //!< Pairing failed due to an unspecified reason +#define SMP_PAIRING_FAILED_REPEATED_ATTEMPTS 0x09 //!< Pairing or authentication procedure is disallowed because too little time has elapsed since the last pairing request or security request. +#define SMP_PAIRING_FAILED_INVALID_PARAMERERS 0x0A //!< The Invalid Parameters error code indicates that the command length is invalid or that a parameter is outside of the specified range. +#define SMP_PAIRING_FAILED_DHKEY_CHECK_FAILED 0x0B //!< Indicates to the remote device that the DHKey Check value received doesn��t match the one calculated by the local device. +#define SMP_PAIRING_FAILED_NUMERIC_COMPARISON 0x0C //!< Indicates that the confirm values in the numeric comparison protocol do not match. +#define SMP_PAIRING_FAILED_KEY_REJECTED 0x0F //!< Indicates that the device chose not to accept a distributed key. + +// GAPBOND_BONDING_FAILURE_DEFINES Bonding Failure Actions +#define GAPBOND_FAIL_NO_ACTION 0x00 //!< Take no action upon unsuccessful bonding +#define GAPBOND_FAIL_INITIATE_PAIRING 0x01 //!< Initiate pairing upon unsuccessful bonding +#define GAPBOND_FAIL_TERMINATE_LINK 0x02 //!< Terminate link upon unsuccessful bonding +#define GAPBOND_FAIL_TERMINATE_ERASE_BONDS 0x03 //!< Terminate link and erase all existing bonds on device upon unsuccessful bonding + +// Device NV Items +#define BLE_NVID_IRK 0x02 //!< The Device's IRK +#define BLE_NVID_CSRK 0x03 //!< The Device's CSRK +#define BLE_NVID_SIGNCOUNTER 0x04 //!< The Device's Sign Counter + +//!< RF Mode BOND NV IDs +#define BLE_NVID_BOND_RF_START 0x10 //!< Start of the RF BOND NV IDs + +// Bonding NV Items - Range 0x20 - 0x6F +#define BLE_NVID_GAP_BOND_START 0x20 //!< Start of the GAP Bond Manager's NV IDs + +// GAP BOND Items +#define GAP_BOND_REC_ID_OFFSET 0 //!< NV ID for the main bonding record +#define GAP_BOND_LOCAL_LTK_OFFSET 1 //!< NV ID for the bonding record's local LTK information +#define GAP_BOND_DEV_LTK_OFFSET 2 //!< NV ID for the bonding records' device LTK information +#define GAP_BOND_DEV_IRK_OFFSET 3 //!< NV ID for the bonding records' device IRK +#define GAP_BOND_DEV_CSRK_OFFSET 4 //!< NV ID for the bonding records' device CSRK +#define GAP_BOND_DEV_SIGN_COUNTER_OFFSET 5 //!< NV ID for the bonding records' device Sign Counter +#define GAP_BOND_REC_IDS 6 + +// Macros to calculate the index/offset in to NV space +#define calcNvID(Idx, offset) (((((Idx) * GAP_BOND_REC_IDS) + (offset))) + BLE_NVID_GAP_BOND_START) +#define mainRecordNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_REC_ID_OFFSET)) +#define localLTKNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_LOCAL_LTK_OFFSET)) +#define devLTKNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_DEV_LTK_OFFSET)) +#define devIRKNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_DEV_IRK_OFFSET)) +#define devCSRKNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_DEV_CSRK_OFFSET)) +#define devSignCounterNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_DEV_SIGN_COUNTER_OFFSET)) + +// GATT Configuration NV Items -Range 0x70 - 0x7F +#define BLE_NVID_GATT_CFG_START 0x70 //!< Start of the GATT Configuration NV IDs + +// Macros to calculate the GATT index/offset in to NV space +#define gattCfgNvID(Idx) ((Idx) + BLE_NVID_GATT_CFG_START) + +#define BLE_NVID_MAX_VAL 0x7F + +// Structure of NV data for the connected device's encryption information +typedef struct +{ + uint8_t LTK[KEYLEN]; // Long Term Key (LTK) + uint16_t div; // LTK eDiv + uint8_t rand[B_RANDOM_NUM_SIZE]; // LTK random number + uint8_t keySize; // LTK key size +} gapBondLTK_t; + +// Structure of NV data for the connected device's address information +typedef struct +{ + uint8_t publicAddr[B_ADDR_LEN]; // Master's address + uint8_t reconnectAddr[B_ADDR_LEN]; // Privacy Reconnection Address + uint16_t stateFlags; // State flags: SM_AUTH_STATE_AUTHENTICATED & SM_AUTH_STATE_BONDING + uint8_t bondsToDelete; +} gapBondRec_t; + +// Structure of NV data for the connected device's characteristic configuration +typedef struct +{ + uint16_t attrHandle; // attribute handle + uint8_t value; // attribute value for this device +} gapBondCharCfg_t; + +/********************************************************************* + * TYPEDEFS + */ +typedef struct +{ + uint8_t srk[KEYLEN]; // Signature Resolving Key + uint32_t signCounter; // Sign Counter +} linkSec_t; + +typedef struct +{ + uint8_t ltk[KEYLEN]; // Long Term Key + uint16_t div; // Diversifier + uint8_t rand[B_RANDOM_NUM_SIZE]; // random number + uint8_t keySize; // LTK Key Size + uint8_t gapBondInvalid; +} encParams_t; + +typedef struct +{ + uint8_t connRole; // GAP Profile Roles @GAP_PROFILE_ROLE_DEFINES + uint8_t addrType; // Address type of connected device + uint8_t addr[B_ADDR_LEN]; // Other Device's address + encParams_t encParams; +} bondEncParams_t; + +typedef struct +{ + uint8_t taskID; // Application that controls the link + uint16_t connectionHandle; // Controller connection handle + uint8_t stateFlags; // LINK_CONNECTED, LINK_AUTHENTICATED... + uint8_t addrType; // Address type of connected device + uint8_t addr[B_ADDR_LEN]; // Other Device's address + uint8_t connRole; // Connection formed as central or peripheral + uint16_t connInterval; // The connection's interval (n * 1.25ms) + uint16_t connLatency; + uint16_t connTimeout; + uint16_t MTU; // The connection's MTU size + linkSec_t sec; // Connection Security related items + encParams_t *pEncParams; // pointer to LTK, ediv, rand. if needed. + uint16_t smEvtID; + void *pPairingParams; + void *pAuthLink; +} linkDBItem_t; + +// function pointer used to register for a status callback +typedef void (*pfnLinkDBCB_t)( uint16_t connectionHandle, uint8_t changeType ); +// function pointer used to perform specialized link database searches +typedef void (*pfnPerformFuncCB_t)( linkDBItem_t *pLinkItem ); + +/** + * Attribute Type format (2 or 16 octet UUID). + */ +typedef struct +{ + uint8_t len; //!< Length of UUID (2 or 16) + uint8_t uuid[ATT_UUID_SIZE]; //!< 16 or 128 bit UUID +} attAttrType_t; + +/** + * Attribute Type format (2-octet Bluetooth UUID). + */ +typedef struct +{ + uint8_t len; //!< Length of UUID (2) + uint8_t uuid[ATT_BT_UUID_SIZE]; //!< 16 bit UUID +} attAttrBtType_t; + +/** + * Error Response format. + */ +typedef struct +{ + uint8_t reqOpcode; //!< Request that generated this error response + uint16_t handle; //!< Attribute handle that generated error response + uint8_t errCode; //!< Reason why the request has generated error response +} attErrorRsp_t; + +/** + * Exchange MTU Request format. + */ +typedef struct +{ + uint16_t clientRxMTU; //!< Client receive MTU size +} attExchangeMTUReq_t; + +/** + * Exchange MTU Response format. + */ +typedef struct +{ + uint16_t serverRxMTU; //!< Server receive MTU size +} attExchangeMTURsp_t; + +/** + * Find Information Request format. + */ +typedef struct +{ + uint16_t startHandle; //!< First requested handle number (must be first field) + uint16_t endHandle; //!< Last requested handle number +} attFindInfoReq_t; + +/** + * Find Information Response format. + */ +typedef struct +{ + uint16_t numInfo; //!< Number of attribute handle-UUID pairs found + uint8_t format; //!< Format of information data + uint8_t *pInfo; //!< Information data whose format is determined by format field (4 to ATT_MTU_SIZE-2) +} attFindInfoRsp_t; + +/** + * Find By Type Value Request format. + */ +typedef struct +{ + uint16_t startHandle; //!< First requested handle number (must be first field) + uint16_t endHandle; //!< Last requested handle number + attAttrBtType_t type; //!< 2-octet UUID to find + uint16_t len; //!< Length of value + uint8_t *pValue; //!< Attribute value to find (0 to ATT_MTU_SIZE-7) +} attFindByTypeValueReq_t; + +/** + * Find By Type Value Response format. + */ +typedef struct +{ + uint16_t numInfo; //!< Number of handles information found + uint8_t *pHandlesInfo; //!< List of 1 or more handles information (4 to ATT_MTU_SIZE-1) +} attFindByTypeValueRsp_t; + +/** + * Read By Type Request format. + */ +typedef struct +{ + uint16_t startHandle; //!< First requested handle number (must be first field) + uint16_t endHandle; //!< Last requested handle number + attAttrType_t type; //!< Requested type (2 or 16 octet UUID) +} attReadByTypeReq_t; + +/** + * Read By Type Response format. + */ +typedef struct +{ + uint16_t numPairs; //!< Number of attribute handle-UUID pairs found + uint16_t len; //!< Size of each attribute handle-value pair + uint8_t *pDataList; //!< List of 1 or more attribute handle-value pairs (2 to ATT_MTU_SIZE-2) +} attReadByTypeRsp_t; + +/** + * Read Request format. + */ +typedef struct +{ + uint16_t handle; //!< Handle of the attribute to be read (must be first field) +} attReadReq_t; + +/** + * Read Response format. + */ +typedef struct +{ + uint16_t len; //!< Length of value + uint8_t *pValue; //!< Value of the attribute with the handle given (0 to ATT_MTU_SIZE-1) +} attReadRsp_t; + +/** + * Read Blob Req format. + */ +typedef struct +{ + uint16_t handle; //!< Handle of the attribute to be read (must be first field) + uint16_t offset; //!< Offset of the first octet to be read +} attReadBlobReq_t; + +/** + * Read Blob Response format. + */ +typedef struct +{ + uint16_t len; //!< Length of value + uint8_t *pValue; //!< Part of the value of the attribute with the handle given (0 to ATT_MTU_SIZE-1) +} attReadBlobRsp_t; + +/** + * Read Multiple Request format. + */ +typedef struct +{ + uint8_t *pHandles; //!< Set of two or more attribute handles (4 to ATT_MTU_SIZE-1) - must be first field + uint16_t numHandles; //!< Number of attribute handles +} attReadMultiReq_t; + +/** + * Read Multiple Response format. + */ +typedef struct +{ + uint16_t len; //!< Length of values + uint8_t *pValues; //!< Set of two or more values (0 to ATT_MTU_SIZE-1) +} attReadMultiRsp_t; + +/** + * Read By Group Type Request format. + */ +typedef struct +{ + uint16_t startHandle; //!< First requested handle number (must be first field) + uint16_t endHandle; //!< Last requested handle number + attAttrType_t type; //!< Requested group type (2 or 16 octet UUID) +} attReadByGrpTypeReq_t; + +/** + * Read By Group Type Response format. + */ +typedef struct +{ + uint16_t numGrps; //!< Number of attribute handle, end group handle and value sets found + uint16_t len; //!< Length of each attribute handle, end group handle and value set + uint8_t *pDataList; //!< List of 1 or more attribute handle, end group handle and value (4 to ATT_MTU_SIZE-2) +} attReadByGrpTypeRsp_t; + +/** + * Write Request format. + */ +typedef struct +{ + uint16_t handle; //!< Handle of the attribute to be written (must be first field) + uint16_t len; //!< Length of value + uint8_t *pValue; //!< Value of the attribute to be written (0 to ATT_MTU_SIZE-3) + uint8_t sig; //!< Authentication Signature status (not included (0), valid (1), invalid (2)) + uint8_t cmd; //!< Command Flag +} attWriteReq_t; + +/** + * Prepare Write Request format. + */ +typedef struct +{ + uint16_t handle; //!< Handle of the attribute to be written (must be first field) + uint16_t offset; //!< Offset of the first octet to be written + uint16_t len; //!< Length of value + uint8_t *pValue; //!< Part of the value of the attribute to be written (0 to ATT_MTU_SIZE-5) - must be allocated +} attPrepareWriteReq_t; + +/** + * Prepare Write Response format. + */ +typedef struct +{ + uint16_t handle; //!< Handle of the attribute that has been read + uint16_t offset; //!< Offset of the first octet to be written + uint16_t len; //!< Length of value + uint8_t *pValue; //!< Part of the value of the attribute to be written (0 to ATT_MTU_SIZE-5) +} attPrepareWriteRsp_t; + +/** + * Execute Write Request format. + */ +typedef struct +{ + uint8_t flags; //!< 0x00 - cancel all prepared writes. + //!< 0x01 - immediately write all pending prepared values. +} attExecuteWriteReq_t; + +/** + * Handle Value Notification format. + */ +typedef struct +{ + uint16_t handle; //!< Handle of the attribute that has been changed (must be first field) + uint16_t len; //!< Length of value + uint8_t *pValue; //!< Current value of the attribute (0 to ATT_MTU_SIZE-3) +} attHandleValueNoti_t; + +/** + * Handle Value Indication format. + */ +typedef struct +{ + uint16_t handle; //!< Handle of the attribute that has been changed (must be first field) + uint16_t len; //!< Length of value + uint8_t *pValue; //!< Current value of the attribute (0 to ATT_MTU_SIZE-3) +} attHandleValueInd_t; + +/** + * ATT Flow Control Violated Event message format. This message is sent to the + * app by the local ATT Server or Client when a sequential ATT Request-Response + * or Indication-Confirmation protocol flow control is violated for a connection. + * All subsequent ATT Requests and Indications received by the local ATT Server + * and Client respectively will be dropped. + * + * This message is to inform the app (that has registered with GAP by calling + * GAP_RegisterForMsgs()) in case it wants to drop the connection. + */ +typedef struct +{ + uint8_t opcode; //!< opcode of message that caused flow control violation + uint8_t pendingOpcode; //!< opcode of pending message +} attFlowCtrlViolatedEvt_t; + +/** + * ATT MTU Updated Event message format. This message is sent to the app + * by the local ATT Server or Client when the ATT MTU size is updated for a + * connection. The default ATT MTU size is 23 octets. + * + * This message is to inform the app (that has registered with GAP by calling + * GAP_RegisterForMsgs()) about the new ATT MTU size negotiated for a connection. + */ +typedef struct +{ + uint16_t MTU; //!< new MTU size +} attMtuUpdatedEvt_t; + +/** + * ATT Message format. It's a union of all attribute protocol messages and + * locally-generated events used between the attribute protocol and upper + * layer profile/application. + */ +typedef union +{ + // Request messages + attExchangeMTUReq_t exchangeMTUReq; //!< ATT Exchange MTU Request + attFindInfoReq_t findInfoReq; //!< ATT Find Information Request + attFindByTypeValueReq_t findByTypeValueReq; //!< ATT Find By Type Value Request + attReadByTypeReq_t readByTypeReq; //!< ATT Read By Type Request + attReadReq_t readReq; //!< ATT Read Request + attReadBlobReq_t readBlobReq; //!< ATT Read Blob Request + attReadMultiReq_t readMultiReq; //!< ATT Read Multiple Request + attReadByGrpTypeReq_t readByGrpTypeReq; //!< ATT Read By Group Type Request + attWriteReq_t writeReq; //!< ATT Write Request + attPrepareWriteReq_t prepareWriteReq; //!< ATT Prepare Write Request + attExecuteWriteReq_t executeWriteReq; //!< ATT Execute Write Request + + // Response messages + attErrorRsp_t errorRsp; //!< ATT Error Response + attExchangeMTURsp_t exchangeMTURsp; //!< ATT Exchange MTU Response + attFindInfoRsp_t findInfoRsp; //!< ATT Find Information Response + attFindByTypeValueRsp_t findByTypeValueRsp; //!< ATT Find By Type Value Response + attReadByTypeRsp_t readByTypeRsp; //!< ATT Read By Type Response + attReadRsp_t readRsp; //!< ATT Read Response + attReadBlobRsp_t readBlobRsp; //!< ATT Read Blob Response + attReadMultiRsp_t readMultiRsp; //!< ATT Read Multiple Response + attReadByGrpTypeRsp_t readByGrpTypeRsp; //!< ATT Read By Group Type Response + attPrepareWriteRsp_t prepareWriteRsp; //!< ATT Prepare Write Response + + // Indication and Notification messages + attHandleValueNoti_t handleValueNoti; //!< ATT Handle Value Notification + attHandleValueInd_t handleValueInd; //!< ATT Handle Value Indication + + // Locally-generated event messages + attFlowCtrlViolatedEvt_t flowCtrlEvt; //!< ATT Flow Control Violated Event + attMtuUpdatedEvt_t mtuEvt; //!< ATT MTU Updated Event +} attMsg_t; + +/** + * GATT Find By Type Value Request format. + */ +typedef struct +{ + uint16_t startHandle; //!< First requested handle number (must be first field) + uint16_t endHandle; //!< Last requested handle number + attAttrType_t value; //!< Primary service UUID value (2 or 16 octets) +} gattFindByTypeValueReq_t; + +/** + * GATT Read By Type Request format. + */ +typedef struct +{ + uint8_t discCharsByUUID; //!< Whether this is a GATT Discover Characteristics by UUID sub-procedure + attReadByTypeReq_t req; //!< Read By Type Request +} gattReadByTypeReq_t; + +/** + * GATT Write Long Request format. Do not change the order of the members. + */ +typedef struct +{ + uint8_t reliable; //!< Whether reliable writes requested (always FALSE for Write Long) + attPrepareWriteReq_t req; //!< ATT Prepare Write Request + uint16_t lastOffset; //!< Offset of last Prepare Write Request sent +} gattWriteLongReq_t; + +/** + * GATT Reliable Writes Request format. Do not change the order of the members. + */ +typedef struct +{ + uint8_t reliable; //!< Whether reliable writes requested (always TRUE for Reliable Writes) + attPrepareWriteReq_t *pReqs; //!< Array of Prepare Write Requests (must be allocated) + uint8_t numReqs; //!< Number of Prepare Write Requests + uint8_t index; //!< Index of last Prepare Write Request sent + uint8_t flags; //!< 0x00 - cancel all prepared writes. + //!< 0x01 - immediately write all pending prepared values. +} gattReliableWritesReq_t; + +/** + * GATT Message format. It's a union of all attribute protocol/profile messages + * and locally-generated events used between the attribute protocol/profile and + * upper layer application. + */ +typedef union +{ + // Request messages + attExchangeMTUReq_t exchangeMTUReq; //!< ATT Exchange MTU Request + attFindInfoReq_t findInfoReq; //!< ATT Find Information Request + attFindByTypeValueReq_t findByTypeValueReq; //!< ATT Find By Type Value Request + attReadByTypeReq_t readByTypeReq; //!< ATT Read By Type Request + attReadReq_t readReq; //!< ATT Read Request + attReadBlobReq_t readBlobReq; //!< ATT Read Blob Request + attReadMultiReq_t readMultiReq; //!< ATT Read Multiple Request + attReadByGrpTypeReq_t readByGrpTypeReq; //!< ATT Read By Group Type Request + attWriteReq_t writeReq; //!< ATT Write Request + attPrepareWriteReq_t prepareWriteReq; //!< ATT Prepare Write Request + attExecuteWriteReq_t executeWriteReq; //!< ATT Execute Write Request + gattFindByTypeValueReq_t gattFindByTypeValueReq;//!< GATT Find By Type Value Request + gattReadByTypeReq_t gattReadByTypeReq; //!< GATT Read By Type Request + gattWriteLongReq_t gattWriteLongReq; //!< GATT Long Write Request + gattReliableWritesReq_t gattReliableWritesReq; //!< GATT Reliable Writes Request + + // Response messages + attErrorRsp_t errorRsp; //!< ATT Error Response + attExchangeMTURsp_t exchangeMTURsp; //!< ATT Exchange MTU Response + attFindInfoRsp_t findInfoRsp; //!< ATT Find Information Response + attFindByTypeValueRsp_t findByTypeValueRsp; //!< ATT Find By Type Value Response + attReadByTypeRsp_t readByTypeRsp; //!< ATT Read By Type Response + attReadRsp_t readRsp; //!< ATT Read Response + attReadBlobRsp_t readBlobRsp; //!< ATT Read Blob Response + attReadMultiRsp_t readMultiRsp; //!< ATT Read Multiple Response + attReadByGrpTypeRsp_t readByGrpTypeRsp; //!< ATT Read By Group Type Response + attPrepareWriteRsp_t prepareWriteRsp; //!< ATT Prepare Write Response + + // Indication and Notification messages + attHandleValueNoti_t handleValueNoti; //!< ATT Handle Value Notification + attHandleValueInd_t handleValueInd; //!< ATT Handle Value Indication + + // Locally-generated event messages + attFlowCtrlViolatedEvt_t flowCtrlEvt; //!< ATT Flow Control Violated Event + attMtuUpdatedEvt_t mtuEvt; //!< ATT MTU Updated Event +} gattMsg_t; + +/** + * GATT tmos GATT_MSG_EVENT message format. This message is used to forward an + * incoming attribute protocol/profile message up to upper layer application. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GATT_MSG_EVENT and status + uint16_t connHandle; //!< Connection message was received on + uint8_t method; //!< Type of message + gattMsg_t msg; //!< Attribute protocol/profile message +} gattMsgEvent_t; + +/** + * GATT Attribute Type format. + */ +typedef struct +{ + uint8_t len; //!< Length of UUID (2 or 16) + const uint8_t *uuid; //!< Pointer to UUID +} gattAttrType_t; + +/** + * GATT Attribute format. + */ +typedef struct attAttribute_t +{ + gattAttrType_t type; //!< Attribute type (2 or 16 octet UUIDs) + uint8_t permissions; //!< Attribute permissions + uint16_t handle; //!< Attribute handle - assigned internally by attribute server + uint8_t *pValue; //!< Attribute value - encoding of the octet array is defined in + //!< the applicable profile. The maximum length of an attribute + //!< value shall be 512 octets. +} gattAttribute_t; + +/** + * GATT Service format. + */ +typedef struct +{ + uint16_t numAttrs; //!< Number of attributes in attrs + uint8_t encKeySize; //!< Minimum encryption key size required by service (7-16 bytes) + + /** Array of attribute records. + * note: The list must start with a Service attribute followed by + * all attributes associated with this Service attribute. + */ + gattAttribute_t *attrs; +} gattService_t; + +/** + * @brief Callback function prototype to read an attribute value. + * + * @note blePending can be returned ONLY for the following + * read operations: + * - Read Request: ATT_READ_REQ + * - Read Blob Request: ATT_READ_BLOB_REQ + * + * @note If blePending is returned then it's the responsibility of the application to respond to + * ATT_READ_REQ and ATT_READ_BLOB_REQ message with ATT_READ_RSP and ATT_READ_BLOB_RSP + * message respectively. + * + * @note Payload 'pValue' used with ATT_READ_RSP and ATT_READ_BLOB_RSP must be allocated using GATT_bm_alloc(). + * + * @param connHandle - connection request was received on + * @param pAttr - pointer to attribute + * @param pValue - pointer to data to be read (to be returned) + * @param pLen - length of data (to be returned) + * @param offset - offset of the first octet to be read + * @param maxLen - maximum length of data to be read + * @param method - type of read message + * + * @return SUCCESS: Read was successfully.
+ * blePending: A response is pending for this client.
+ * Error, otherwise: ref ATT_ERR_CODE_DEFINES.
+ */ +typedef uint8_t (*pfnGATTReadAttrCB_t)( uint16_t connHandle, gattAttribute_t *pAttr, uint8_t *pValue, + uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method ); + +/** + * @brief Callback function prototype to write an attribute value. + * + * @note blePending can be returned ONLY for the following + * write operations: + * - Write Request: ATT_WRITE_REQ + * - Write Command: ATT_WRITE_CMD + * - Write Long: ATT_EXECUTE_WRITE_REQ + * - Reliable Writes: Multiple ATT_PREPARE_WRITE_REQ followed by one final ATT_EXECUTE_WRITE_REQ + * + * @note If blePending is returned then it's the responsibility of the application to 1) respond to + * ATT_WRITE_REQ and ATT_EXECUTE_WRITE_REQ message with ATT_WRITE_RSP and ATT_EXECUTE_WRITE_RSP + * message respectively, and 2) free each request payload 'pValue' using BM_free(). + * + * @note Write Command (ATT_WRITE_CMD) does NOT require a response message. + * + * @param connHandle - connection request was received on + * @param pAttr - pointer to attribute + * @param pValue - pointer to data to be written + * @param pLen - length of data + * @param offset - offset of the first octet to be written + * @param method - type of write message + * + * @return SUCCESS: Write was successfully.
+ * blePending: A response is pending for this client.
+ * Error, otherwise: ref ATT_ERR_CODE_DEFINES.
+ */ +typedef uint8_t (*pfnGATTWriteAttrCB_t)( uint16_t connHandle, gattAttribute_t *pAttr, uint8_t *pValue, + uint16_t len, uint16_t offset, uint8_t method ); + +/** + * @brief Callback function prototype to authorize a Read or Write operation + * on a given attribute. + * + * @param connHandle - connection request was received on + * @param pAttr - pointer to attribute + * @param opcode - request opcode (ATT_READ_REQ or ATT_WRITE_REQ) + * + * @return SUCCESS: Operation authorized.
+ * ATT_ERR_INSUFFICIENT_AUTHOR: Authorization required.
+ */ +typedef bStatus_t (*pfnGATTAuthorizeAttrCB_t)( uint16_t connHandle, gattAttribute_t *pAttr, + uint8_t opcode ); + +/** + * GATT Structure for Client Characteristic Configuration. + */ +typedef struct +{ + uint16_t connHandle; //!< Client connection handle + uint8_t value; //!< Characteristic configuration value for this client +} gattCharCfg_t; + +/** + * GATT Structure for service callback functions - must be setup by the application + * and used when GATTServApp_RegisterService() is called. + */ +typedef struct +{ + pfnGATTReadAttrCB_t pfnReadAttrCB; //!< Read callback function pointer + pfnGATTWriteAttrCB_t pfnWriteAttrCB; //!< Write callback function pointer + pfnGATTAuthorizeAttrCB_t pfnAuthorizeAttrCB; //!< Authorization callback function pointer +} gattServiceCBs_t; + +/*************************************gap**************************************/ +/** + * Connection parameters for the peripheral device. These numbers are used + * to compare against connection events and request connection parameter + * updates with the central. + */ +typedef struct +{ + uint16_t intervalMin; //!< Minimum value for the connection event (interval. 0x0006 - 0x0C80 * 1.25ms) + uint16_t intervalMax; //!< Maximum value for the connection event (interval. 0x0006 - 0x0C80 * 1.25ms) + uint16_t latency; //!< Number of LL latency connection events (0x0000 - 0x03e8) + uint16_t timeout; //!< Connection Timeout (0x000A - 0x0C80 * 10ms) +} gapPeriConnectParams_t; + +/** + * GAP event header format. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP type of command. Ref: @ref GAP_MSG_EVENT_DEFINES +} gapEventHdr_t; + +/** + * GAP_DEVICE_INIT_DONE_EVENT message format. This message is sent to the + * app when the Device Initialization is done [initiated by calling + * GAP_DeviceInit()]. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_DEVICE_INIT_DONE_EVENT + uint8_t devAddr[B_ADDR_LEN]; //!< Device's BD_ADDR + uint16_t dataPktLen; //!< HC_LE_Data_Packet_Length + uint8_t numDataPkts; //!< HC_Total_Num_LE_Data_Packets +} gapDeviceInitDoneEvent_t; + +/** + * GAP_SIGNATURE_UPDATED_EVENT message format. This message is sent to the + * app when the signature counter has changed. This message is to inform the + * application in case it wants to save it to be restored on reboot or reconnect. + * This message is sent to update a connection's signature counter and to update + * this device's signature counter. If devAddr == BD_ADDR, then this message pertains + * to this device. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_SIGNATURE_UPDATED_EVENT + uint8_t addrType; //!< Device's address type for devAddr + uint8_t devAddr[B_ADDR_LEN]; //!< Device's BD_ADDR, could be own address + uint32_t signCounter; //!< new Signed Counter +} gapSignUpdateEvent_t; + +/** + * GAP_DEVICE_INFO_EVENT message format. This message is sent to the + * app during a Device Discovery Request, when a new advertisement or scan + * response is received. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_DEVICE_INFO_EVENT + uint8_t eventType; //!< Advertisement Type: @ref GAP_ADVERTISEMENT_REPORT_TYPE_DEFINES + uint8_t addrType; //!< address type: @ref GAP_ADDR_TYPE_DEFINES + uint8_t addr[B_ADDR_LEN]; //!< Address of the advertisement or SCAN_RSP + int8_t rssi; //!< Advertisement or SCAN_RSP RSSI + uint8_t dataLen; //!< Length (in bytes) of the data field (evtData) + uint8_t *pEvtData; //!< Data field of advertisement or SCAN_RSP +} gapDeviceInfoEvent_t; + +/** + * GAP_DIRECT_DEVICE_INFO_EVENT message format. This message is sent to the + * app during a Device Discovery Request, when a new advertisement or scan + * response is received. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_DIRECT_DEVICE_INFO_EVENT + uint8_t eventType; //!< Advertisement Type: @ref GAP_ADVERTISEMENT_REPORT_TYPE_DEFINES + uint8_t addrType; //!< address type: @ref GAP_ADDR_TYPE_DEFINES + uint8_t addr[B_ADDR_LEN]; //!< Address of the advertisement or SCAN_RSP + uint8_t directAddrType; //!< public or random address type + uint8_t directAddr[B_ADDR_LEN]; //!< device address + int8_t rssi; //!< Advertisement or SCAN_RSP RSSI +} gapDirectDeviceInfoEvent_t; + +/** + * GAP_EXT_ADV_DEVICE_INFO_EVENT message format. This message is sent to the + * app during a Device Discovery Request, when a new advertisement or scan + * response is received. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_EXT_ADV_DEVICE_INFO_EVENT + uint8_t eventType; //!< Advertisement Type: @ref GAP_ADVERTISEMENT_REPORT_TYPE_DEFINES + uint8_t addrType; //!< address type: @ref GAP_ADDR_TYPE_DEFINES + uint8_t addr[B_ADDR_LEN]; //!< Address of the advertisement or SCAN_RSP + uint8_t primaryPHY; //!< Advertiser PHY on the primary advertising channel + uint8_t secondaryPHY; //!< Advertiser PHY on the secondary advertising channel + uint8_t advertisingSID; //!< Value of the Advertising SID subfield in the ADI field of the PDU + int8_t txPower; //!< Advertisement or SCAN_RSP power + int8_t rssi; //!< Advertisement or SCAN_RSP RSSI + uint16_t periodicAdvInterval; //!< the interval of periodic advertising + uint8_t directAddressType; //!< public or random address type + uint8_t directAddress[B_ADDR_LEN]; //!< device address + uint8_t dataLen; //!< Length (in bytes) of the data field (evtData) + uint8_t *pEvtData; //!< Data field of advertisement or SCAN_RSP +} gapExtAdvDeviceInfoEvent_t; + +/** + * Type of device discovery (Scan) to perform. + */ +typedef struct +{ + uint8_t taskID; //!< Requesting App's Task ID, used to return results + uint8_t mode; //!< Discovery Mode: @ref GAP_DEVDISC_MODE_DEFINES + uint8_t activeScan; //!< TRUE for active scanning + uint8_t whiteList; //!< TRUE to only allow advertisements from devices in the white list. +} gapDevDiscReq_t; + +/** + * Type of device. + */ +typedef struct +{ + uint8_t eventType; //!< Indicates advertising event type used by the advertiser: @ref GAP_ADVERTISEMENT_REPORT_TYPE_DEFINES + uint8_t addrType; //!< Address Type: @ref GAP_ADDR_TYPE_DEFINES + uint8_t addr[B_ADDR_LEN]; //!< Device's Address +} gapDevRec_t; + +/** + * GAP_DEVICE_DISCOVERY_EVENT message format. This message is sent to the + * Application after a scan is performed. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_DEVICE_DISCOVERY_EVENT + uint8_t numDevs; //!< Number of devices found during scan + gapDevRec_t *pDevList; //!< array of device records +} gapDevDiscEvent_t; + +/** + * GAP_MAKE_DISCOVERABLE_DONE_EVENT message format. This message is sent to the + * app when the Advertise config is complete. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_MAKE_DISCOVERABLE_DONE_EVENT +} gapMakeDiscoverableRspEvent_t; + +/** + * GAP_END_DISCOVERABLE_DONE_EVENT message format. This message is sent to the + * app when the Advertising has stopped. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_END_DISCOVERABLE_DONE_EVENT +} gapEndDiscoverableRspEvent_t; + +/** + * GAP_PERIODIC_ADVERTISING_DONE_EVENT message format. This message is sent to the + * app when the Periodic Advertising config is complete. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_PERIODIC_ADVERTISING_DONE_EVENT +} gapMakePeriodicRspEvent_t; + +/** + * GAP_END_PERIODIC_ADV_DONE_EVENT message format. This message is sent to the + * app when the Periodic Advertising disable is complete. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_END_PERIODIC_ADV_DONE_EVENT +} gapEndPeriodicRspEvent_t; + +/** + * GAP_SYNC_ESTABLISHED_EVENT message format. This message is sent to the + * app when the Periodic Advertising Sync Establish is complete. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_SYNC_ESTABLISHED_EVENT + uint8_t status; //!< Periodic advertising sync status + uint16_t syncHandle; //!< Identifying the periodic advertising train + uint8_t advertisingSID; //!< Value of the Advertising SID subfield in the ADI field of the PDU + uint8_t devAddrType; //!< Device address type: @ref GAP_ADDR_TYPE_DEFINES + uint8_t devAddr[B_ADDR_LEN]; //!< Device address of sync + uint8_t advertisingPHY; //!< Advertiser PHY + uint16_t periodicInterval; //!< Periodic advertising interval + uint8_t clockAccuracy; //!< Clock Accuracy +} gapSyncEstablishedEvent_t; + +/** + * GAP_PERIODIC_ADV_DEVICE_INFO_EVENT message format. This message is sent to the + * app during Periodic Advertising Sync, when received a Periodic Advertising packet + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_PERIODIC_ADV_DEVICE_INFO_EVENT + uint16_t syncHandle; //!< Identifying the periodic advertising train + int8_t txPower; //!< Periodic advertising tx power,Units: dBm + int8_t rssi; //!< Periodic advertising rssi,Units: dBm + uint8_t unUsed; + uint8_t dataStatus; //!< Data complete + uint8_t dataLength; //!< Length (in bytes) of the data field (evtData) + uint8_t *pEvtData; //!< Data field of periodic advertising data +} gapPeriodicAdvDeviceInfoEvent_t; + +/** + * GAP_SYNC_LOST_EVENT message format. This message is sent to the + * app when the Periodic Advertising Sync timeout period. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_SYNC_LOST_EVENT + uint16_t syncHandle; //!< Identifying the periodic advertising train +} gapSyncLostEvent_t; + +/** + * GAP_SCAN_REQUEST_EVENT message format. This message is sent to the + * app when the advertiser receives a SCAN_REQ PDU or an AUX_SCAN_REQ PDU + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_SCAN_REQUEST_EVENT + uint8_t advHandle; //!< identifying the periodic advertising train + uint8_t scannerAddrType; //!< the type of the address + uint8_t scannerAddr[B_ADDR_LEN];//!< the address of scanner device +} gapScanReqReseiveEvent_t; + +/** + * GAP_ADV_DATA_UPDATE_DONE_EVENT message format. This message is sent to the + * app when Advertising Data Update is complete. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_ADV_DATA_UPDATE_DONE_EVENT + uint8_t adType; //!< TRUE if advertising data, FALSE if SCAN_RSP +} gapAdvDataUpdateEvent_t; + +/** + * GAP_LINK_ESTABLISHED_EVENT message format. This message is sent to the app + * when the link request is complete.
+ *
+ * For an Observer, this message is sent to complete the Establish Link Request.
+ * For a Peripheral, this message is sent to indicate that a link has been created. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_LINK_ESTABLISHED_EVENT + uint8_t devAddrType; //!< Device address type: @ref GAP_ADDR_TYPE_DEFINES + uint8_t devAddr[B_ADDR_LEN]; //!< Device address of link + uint16_t connectionHandle; //!< Connection Handle from controller used to ref the device + uint8_t connRole; //!< Connection formed as Master or Slave + uint16_t connInterval; //!< Connection Interval + uint16_t connLatency; //!< Connection Latency + uint16_t connTimeout; //!< Connection Timeout + uint8_t clockAccuracy; //!< Clock Accuracy +} gapEstLinkReqEvent_t; + +/** + * GAP_LINK_PARAM_UPDATE_EVENT message format. This message is sent to the app + * when the connection parameters update request is complete. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_LINK_PARAM_UPDATE_EVENT + uint8_t status; //!< bStatus_t + uint16_t connectionHandle; //!< Connection handle of the update + uint16_t connInterval; //!< Requested connection interval + uint16_t connLatency; //!< Requested connection latency + uint16_t connTimeout; //!< Requested connection timeout +} gapLinkUpdateEvent_t; + +/** + * GAP_LINK_TERMINATED_EVENT message format. This message is sent to the + * app when a link to a device is terminated. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_LINK_TERMINATED_EVENT + uint16_t connectionHandle; //!< connection Handle + uint8_t reason; //!< termination reason from LL + uint8_t connRole; +} gapTerminateLinkEvent_t; + +/** + * GAP_PHY_UPDATE_EVENT message format. This message is sent to the app(GAP_MSG_EVENT) + * when the PHY update request is complete. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_PHY_UPDATE_EVENT + uint8_t status; //!< bStatus_t + uint16_t connectionHandle; //!< Connection handle of the update + uint8_t connTxPHYS; //!< tx phy(GAP_PHY_VAL_TYPE) + uint8_t connRxPHYS; //!< rx phy(GAP_PHY_VAL_TYPE) +} gapPhyUpdateEvent_t; + +/** + * GAP_PASSKEY_NEEDED_EVENT message format. This message is sent to the + * app when a Passkey is needed from the app's user interface. + */ +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_PASSKEY_NEEDED_EVENT + uint8_t deviceAddr[B_ADDR_LEN]; //!< address of device to pair with, and could be either public or random. + uint16_t connectionHandle; //!< Connection handle + uint8_t uiInputs; //!< Pairing User Interface Inputs - Ask user to input passcode + uint8_t uiOutputs; //!< Pairing User Interface Outputs - Display passcode +} gapPasskeyNeededEvent_t; + +/** + * Passcode Callback Function + */ +typedef void (*pfnPasscodeCB_t)( uint8_t *deviceAddr, //!< address of device to pair with, and could be either public or random. + uint16_t connectionHandle, //!< Connection handle + uint8_t uiInputs, //!< Pairing User Interface Inputs - Ask user to input passcode + uint8_t uiOutputs //!< Pairing User Interface Outputs - Display passcode + ); + +/** + * Pairing State Callback Function + */ +typedef void (*pfnPairStateCB_t)( uint16_t connectionHandle, //!< Connection handle + uint8_t state, //!< Pairing state @ref GAPBOND_PAIRING_STATE_DEFINES + uint8_t status //!< Pairing status + ); + +typedef struct +{ + tmos_event_hdr_t hdr; //!< GAP_MSG_EVENT and status + uint8_t opcode; //!< GAP_O0B_NEEDED_EVENT + uint8_t deviceAddr[B_ADDR_LEN]; //!< address of device to pair with, and could be either public or random. + uint16_t connectionHandle; //!< Connection handle + uint8_t r_local[16]; + uint8_t c_local[16]; +} gapOobNeededEvent_t; + +/** + * OOB Callback Function + */ +typedef void (*pfnOobCB_t)( uint8_t *deviceAddr, //!< address of device to pair with, and could be either public or random. + uint16_t connectionHandle, //!< Connection handle + uint8_t* r_local, //!< local rand + uint8_t *c_local //!< local confirm + ); + +/** + * Callback Registration Structure + */ +typedef struct +{ + pfnPasscodeCB_t passcodeCB; //!< Passcode callback + pfnPairStateCB_t pairStateCB; //!< Pairing state callback + pfnOobCB_t oobCB; //!< oob callback +} gapBondCBs_t; + +typedef int (*pfnEcc_key_t)( uint8_t *pub, uint8_t *priv); + +typedef int (*pfnEcc_dhkey_t)( uint8_t *peer_pub_key_x, uint8_t *peer_pub_key_y, + uint8_t *our_priv_key, uint8_t *out_dhkey ); + +typedef int (*pfnEcc_alg_f4_t)( uint8_t *u, uint8_t *v, uint8_t *x, uint8_t z, + uint8_t *out_enc_data ); + +typedef int (*pfnEcc_alg_g2_t)( uint8_t *u, uint8_t *v, uint8_t *x, uint8_t *y, + uint32_t *passkey ); + +typedef int (*pfnEcc_alg_f5_t)( uint8_t *w, uint8_t *n1, uint8_t *n2, + uint8_t a1t, uint8_t *a1, uint8_t a2t, uint8_t *a2, uint8_t *mackey, uint8_t *ltk ); + +typedef int (*pfnEcc_alg_f6_t)( uint8_t *w, uint8_t *n1, uint8_t *n2, uint8_t *r, + uint8_t *iocap, uint8_t a1t, uint8_t *a1, uint8_t a2t, uint8_t *a2, uint8_t *check ); + +/** + * Callback Registration Structure + */ +typedef struct +{ + pfnEcc_key_t gen_key_pair; + pfnEcc_dhkey_t gen_dhkey; + pfnEcc_alg_f4_t alg_f4; //!< LE Secure Connections confirm value generation function f4 + pfnEcc_alg_g2_t alg_g2; //!< LE Secure Connections numeric comparison value generation function g2 + pfnEcc_alg_f5_t alg_f5; //!< LE Secure Connect ions key generation function f5 + pfnEcc_alg_f6_t alg_f6; //!< LE Secure Connections check value generation function f6 +} gapEccCBs_t; + +/** + * gapRole_States_t defined + */ +typedef unsigned long gapRole_States_t; + +// gapRole_States_t @ 4b'[3-0]-advertising states +#define GAPROLE_STATE_ADV_MASK (0xF) //!< advertising states mask +#define GAPROLE_STATE_ADV_SHIFT (0x0) //!< advertising states shift +#define GAPROLE_INIT 0 //!< Waiting to be started +#define GAPROLE_STARTED 1 //!< Started but not advertising +#define GAPROLE_ADVERTISING 2 //!< Currently Advertising +#define GAPROLE_WAITING 3 //!< Device is started but not advertising, is in waiting period before advertising again +#define GAPROLE_CONNECTED 4 //!< In a connection +#define GAPROLE_CONNECTED_ADV 5 //!< In a connection + advertising +#define GAPROLE_ERROR 6 //!< Error occurred - invalid state + +// gapRole_States_t @ 4b'[7-4]-Periodic advertising states +// Periodic advertising Enable,only effective when GAP_ADTYPE_EXT_NONCONN_NONSCAN_UNDIRECT advertising event enable +#define GAPROLE_STATE_PERIODIC_MASK (0xF0) //!< Periodic advertising states mask +#define GAPROLE_STATE_PERIODIC_SHIFT (4) //!< Periodic advertising states shift +#define GAPROLE_PERIODIC_INVALID (0<<4) //!< Periodic advertising Waiting to be started +#define GAPROLE_PERIODIC_ENABLE (1<<4) //!< Periodic advertising Enable +#define GAPROLE_PERIODIC_WAIT (2<<4) //!< Periodic advertising is started but disable +#define GAPROLE_PERIODIC_ERROR (3<<4) //!< Periodic advertising error occurred + +// gapRole_States_t @ 16b'[23-8]- Reserved for future use + +// gapRole_States_t @ 8b'[31-24] - indicates which fields change +#define GAPROLE_PERIODIC_STATE_VALID (1<<24) //!< indicates periodic advertising states change + +/** + * gapRole Event Structure + */ +typedef union +{ + gapEventHdr_t gap; //!< GAP_MSG_EVENT and status. + gapDeviceInitDoneEvent_t initDone; //!< GAP initialization done. + gapDeviceInfoEvent_t deviceInfo; //!< Discovery device information event structure. + gapDirectDeviceInfoEvent_t deviceDirectInfo; //!< Discovery direct device information event structure. + gapAdvDataUpdateEvent_t dataUpdate; //!< Advertising Data Update is complete. + gapPeriodicAdvDeviceInfoEvent_t devicePeriodicInfo; //!< Discovery periodic device information event structure. + gapExtAdvDeviceInfoEvent_t deviceExtAdvInfo; //!< Discovery extend advertising device information event structure. + gapDevDiscEvent_t discCmpl; //!< Discovery complete event structure. + gapSyncEstablishedEvent_t syncEstEvt; //!< sync established event structure. + gapSyncLostEvent_t syncLostEvt; //!< sync lost event structure. + gapScanReqReseiveEvent_t scanReqEvt; //!< Scan_Request_Received event structure. + + gapEstLinkReqEvent_t linkCmpl; //!< Link complete event structure. + gapLinkUpdateEvent_t linkUpdate; //!< Link update event structure. + gapTerminateLinkEvent_t linkTerminate; //!< Link terminated event structure. + gapPhyUpdateEvent_t linkPhyUpdate; //!< Link phy update event structure. +} gapRoleEvent_t; + +/** + * Type of device. + */ +typedef struct +{ + uint8_t eventType; //!< Indicates advertising event type used by the advertiser: @ref GAP_ADVERTISEMENT_REPORT_TYPE_DEFINES + uint8_t addrType; //!< Scan Address Type:0x00-Public Device Address or Public Identity Address 0x01-Random Device Address or Random (static) Identity Address + uint8_t addr[B_ADDR_LEN]; //!< Device's Address + int8_t rssi; +} gapScanRec_t; + +/** + * Type of GAPRole_CreateSync command parameters. + */ +typedef struct +{ + uint8_t options; + /* + bit0: used to determine whether the Periodic Advertiser List is used + 0: Use the Advertising_SID, Advertisier_Address_Type, and Advertiser_Address parameters to determine which advertiser to listen to. + 1: Use the Periodic Advertiser List to determine which advertiser to listen to. + bit1: whether GAP_PERIODIC_ADV_DEVICE_INFO_EVENT events for this periodic advertising train are initially enabled or disabled. + 0: Reporting initially enabled + 1: Reporting initially disabled + bit2: + 0: Duplicate filtering initially disabled + 1: Duplicate filtering initially enabled */ + uint8_t advertising_SID; //!< if used, specifies the value that must match the Advertising SID + uint8_t addrType; //!< Scan Address Type: @ref GAP_ADDR_TYPE_DEFINES + uint8_t addr[B_ADDR_LEN]; //!< Device's Address + uint16_t skip; //!< the maximum number of consecutive periodic advertising events that the receiver may skip after + //!< successfully receiving a periodic advertising packet.Range: 0x0000 to 0x01F3 + uint16_t syncTimeout; //!< the maximum permitted time between successful receives. If this time is exceeded, synchronization is lost. + //!< Time = N*10 ms.Range: 0x000A to 0x4000 + uint8_t syncCTEType; //!< specifies whether to only synchronize to periodic advertising with certain types of Constant Tone Extension + //!< (a value of 0 indicates that the presence or absence of a Constant Tone Extension is irrelevant). +} gapCreateSync_t; + +/** + * Type of GAPRole_SetPathLossReporting command parameters. + */ +typedef struct +{ + uint16_t connHandle; //!< Used to identify the Connection handle + int8_t highThreshold; //!< High threshold for the path loss.Units: dB + int8_t highHysteresis; //!< Hysteresis value for the high threshold.Units: dB + int8_t lowThreshold; //!< High threshold for the path loss.Units: dB + int8_t lowHysteresis; //!< Hysteresis value for the high threshold.Units: dB + uint16_t minTimeSpent; //!< Minimum time in number of connection events to be observed + //!< once the path crosses the threshold before an event is generated. + uint8_t enable; //!< 0x00:Reporting disabled 0x01:Reporting enabled +} gapRoleSetPathLossReporting_t; + +typedef struct +{ + uint16_t connHandle; //!< Used to identify the Connection handle + int8_t lowRxThreshold; //!< High threshold for the peer power levels.Units: dB + int8_t highRxThreshold; //!< High threshold for the peer power levels.Units: dB + int8_t minTxPower; //!< Minimum transmit power level.Units: dB + int8_t maxTxPower; //!< Maximum transmit power level.Units: dB +} gapRolePowerlevelManagement_t; + +/** + * Callback when the device has been started. Callback event to + * the Notify of a state change. + */ +typedef void (*gapRolesBroadcasterStateNotify_t)( gapRole_States_t newState ); + +typedef void (*gapRolesScanReqRecv_t)( gapScanRec_t * pEvent ); + +typedef struct +{ + gapRolesBroadcasterStateNotify_t pfnStateChange; //!< Whenever the device changes state + gapRolesScanReqRecv_t pfnScanRecv; +} gapRolesBroadcasterCBs_t; + +/** + * Observer Event Callback Function + */ +typedef void (*pfnGapObserverRoleEventCB_t)( gapRoleEvent_t *pEvent //!< Pointer to event structure. + ); + +/** + * Observer Callback Structure + */ +typedef struct +{ + pfnGapObserverRoleEventCB_t eventCB; //!< Event callback. +} gapRoleObserverCB_t; + +/** + * Callback when the device has read an new RSSI value during a connection. + */ +typedef void (*gapRolesRssiRead_t)( uint16_t connHandle, int8_t newRSSI ); + +/** + * Callback when the device has been started. Callback event to + * the Notify of a state change. + */ +typedef void (*gapRolesStateNotify_t)( gapRole_States_t newState, gapRoleEvent_t * pEvent ); + +/** + * Callback when the connection parameteres are updated. + */ +typedef void (*gapRolesParamUpdateCB_t)( uint16_t connHandle, uint16_t connInterval, + uint16_t connSlaveLatency, uint16_t connTimeout ); + +/** + * Callback structure - must be setup by the application and used when gapRoles_StartDevice() is called. + */ +typedef struct +{ + gapRolesStateNotify_t pfnStateChange; //!< Whenever the device changes state + gapRolesRssiRead_t pfnRssiRead; //!< When a valid RSSI is read from controller + gapRolesParamUpdateCB_t pfnParamUpdate; //!< When the connection parameteres are updated +} gapRolesCBs_t; + +/** + * Central Event Callback Function + */ +typedef void (*pfnGapCentralRoleEventCB_t)( gapRoleEvent_t *pEvent ); //!< Pointer to event structure. + +/** + * HCI Data Length Change Event Callback Function + */ +typedef void (*pfnHciDataLenChangeEvCB_t)( uint16_t connHandle, uint16_t maxTxOctets, + uint16_t maxRxOctets ); + +/** + * Central Callback Structure + */ +typedef struct +{ + gapRolesRssiRead_t rssiCB; //!< RSSI callback. + pfnGapCentralRoleEventCB_t eventCB; //!< Event callback. + pfnHciDataLenChangeEvCB_t ChangCB; //!< Length Change Event Callback . +} gapCentralRoleCB_t; // gapCentralRoleCB_t + +/* RF-PHY define */ + +/* + * RF_ROLE_STATUS_TYPE pfnRFStatusCB_t state defined + */ +// TX_MODE call RF_Tx +#define TX_MODE_TX_FINISH 0x01 //!< basic or auto tx mode sends data successfully + //!< if it is in basic mode,it will enter idle state; + //!< if it is in auto mode,it will wait for receiving +#define TX_MODE_TX_FAIL 0x11 //!< basic or auto tx mode fail to send data and enter idle state +#define TX_MODE_TX_TIMEOUT TX_MODE_TX_FAIL //!< time of data transmission +#define TX_MODE_RX_DATA 0x02 //!< auto tx mode receive data(ack) and enter idle state +#define TX_MODE_RX_TIMEOUT 0x12 //!< auto tx mode receive timeout and enter idle state +#define TX_MODE_HOP_SHUT 0x22 + +// RX_MODE call RF_Rx +#define RX_MODE_RX_DATA 0x03 //!< basic or auto rx mode receive data + //!< if it is in basic mode,it will enter idle state; + //!< if it is in auto mode,it will judge whether the type matches; + //!< if it matches,it will send data(ack),otherwise(rsr=2), it will restart receiving +#define RX_MODE_TX_FINISH 0x04 //!< auto rx mode sends data(ack) successfully and enters idle state +#define RX_MODE_TX_FAIL 0x14 //!< auto rx mode fail to send data and enter idle state +#define RX_MODE_TX_TIMEOUT RX_MODE_TX_FAIL //!< time of data transmission +#define RX_MODE_HOP_SHUT 0x24 + +// LLE_MODE_TYPE +#define LLE_MODE_BASIC (0) //!< basic mode, enter idle state after sending or receive +#define LLE_MODE_AUTO (1) //!< auto mode, auto swtich to the receiving status after sending and the sending status after receiving + +// LLE_WHITENING_TYPE +#define LLE_WHITENING_ON (0<<1) +#define LLE_WHITENING_OFF (1<<1) + +// LLE_PHY_TYPE +#define LLE_MODE_PHY_MODE_MASK (0x30) +#define LLE_MODE_PHY_1M (0<<4) +#define LLE_MODE_PHY_2M (1<<4) +#define LLE_MODE_PHY_CODED_S8 (2<<4) +#define LLE_MODE_PHY_CODED_S2 (3<<4) + +#define LLE_MODE_EX_CHANNEL (1<<6) + +#define LLE_MODE_NON_RSSI (1<<7) + +/** + * RFRole Event Callback Function + */ +typedef void (*pfnRFStatusCB_t)( uint8_t sta, uint8_t rsr, uint8_t *rxBuf ); +// sta - current status@ref RF_ROLE_STATUS_TYPE +// rsr - receive status: bit0- crc check result,bit1- type matching result +// rxBuf - receive data buffer + +typedef struct tag_rf_config +{ + uint8_t LLEMode; //!< BIT0 0=basic, 1=auto def@LLE_MODE_TYPE + //!< BIT1 0=whitening on, 1=whitening off def@LLE_WHITENING_TYPE + //!< BIT4-5 00-1M 01-2M 10-coded(S8) 11-coded(S2) def@LLE_PHY_TYPE + //!< BIT6 0=data channel(0-39) + //!< 1=rf frequency (2400000kHz-2483500kHz) + //!< BIT7 0=the first byte of the receive buffer is rssi + //!< 1=the first byte of the receive buffer is package type + uint8_t Channel; //!< rf channel(0-39) + uint32_t Frequency; //!< rf frequency (2400000kHz-2483500kHz) + uint32_t accessAddress; //!< access address,32bit PHY address + uint32_t CRCInit; //!< crc initial value + pfnRFStatusCB_t rfStatusCB; //!< status call back + uint32_t ChannelMap; //!< indicating Used and Unused data channels.Every channel is represented with a + //!< bit positioned as per the data channel index,The LSB represents data channel index 0 + uint8_t Resv; + uint8_t HeartPeriod; //!< The heart package interval shall be an integer multiple of 100ms + uint8_t HopPeriod; //!< hop period( T=32n*RTC clock ),default is 8 + uint8_t HopIndex; //!< indicate the hopIncrement used in the data channel selection algorithm,default is 17 + uint8_t RxMaxlen; //!< Maximum data length received in rf-mode(default 251) + uint8_t TxMaxlen; //!< Maximum data length transmit in rf-mode(default 251) +} rfConfig_t; + +/* end define@RF-PHY */ + +/******************************************************************************* + * UUID defined + */ +/** + * GATT Services + */ +extern const uint8_t gapServiceUUID[]; +extern const uint8_t gattServiceUUID[]; + +/** + * GATT Attribute Types + */ +extern const uint8_t primaryServiceUUID[]; +extern const uint8_t secondaryServiceUUID[]; +extern const uint8_t includeUUID[]; +extern const uint8_t characterUUID[]; + +/** + * GATT Characteristic Descriptors + */ +extern const uint8_t charExtPropsUUID[]; +extern const uint8_t charUserDescUUID[]; +extern const uint8_t clientCharCfgUUID[]; +extern const uint8_t servCharCfgUUID[]; +extern const uint8_t charFormatUUID[]; +extern const uint8_t charAggFormatUUID[]; +extern const uint8_t validRangeUUID[]; +extern const uint8_t extReportRefUUID[]; +extern const uint8_t reportRefUUID[]; + +/** + * GATT Characteristic Types + */ +extern const uint8_t deviceNameUUID[]; +extern const uint8_t appearanceUUID[]; +extern const uint8_t periPrivacyFlagUUID[]; +extern const uint8_t reconnectAddrUUID[]; +extern const uint8_t periConnParamUUID[]; +extern const uint8_t serviceChangedUUID[]; +extern const uint8_t centAddrResUUID[]; + +/******************************************************************************* + * PUBLIC FUNCTIONS + */ +extern uint32_t tmos_rand( void ); // pseudo-random number +extern BOOL tmos_memcmp( const void *src1, const void *src2, uint32_t len ); // TRUE - same, FALSE - different +extern BOOL tmos_isbufset( uint8_t *buf, uint8_t val, uint32_t len ); // TRUE if all "val",FALSE otherwise +extern uint32_t tmos_strlen( char *pString ); +extern void tmos_memset( void * pDst, uint8_t Value, uint32_t len ); +extern void tmos_memcpy( void *dst, const void *src, uint32_t len ); // Generic memory copy. + +/** + * @brief start a event immediately + * + * @param taskID - task ID of event + * @param event - event value + * + * @return 0 - SUCCESS. + */ +extern bStatus_t tmos_set_event( tmosTaskID taskID, tmosEvents event ); + +/** + * @brief clear a event already timeout, cannot be used in it own event function. + * + * @param taskID - task ID of event + * @param event - event value + * + * @return 0 - SUCCESS. + */ +extern bStatus_t tmos_clear_event( tmosTaskID taskID, tmosEvents event ); + +/** + * @brief start a event after period of time + * + * @param taskID - task ID to set event for + * @param event - event to be notified with + * @param time - timeout value + * + * @return TRUE,FALSE. + */ +extern BOOL tmos_start_task( tmosTaskID taskID, tmosEvents event, tmosTimer time ); + +/** + * @brief This function is called to start a timer to expire in n system clock time. + * When the timer expires, the calling task will get the specified event + * and the timer will be reloaded with the timeout value. + * + * @param taskID - task ID to set timer for + * @param event - event to be notified with + * @param time - timeout value + * + * @return SUCCESS, or NO_TIMER_AVAIL. + */ +extern bStatus_t tmos_start_reload_task( tmosTaskID taskID, tmosEvents event, tmosTimer time ); + +/** + * @brief stop a event + * + * @param taskID - task ID of event + * @param event - event value + * + * @param None. + * + * @return SUCCESS. + */ +extern bStatus_t tmos_stop_task( tmosTaskID taskID, tmosEvents event ); + +/** + * @brief get last period of time for this event + * + * @param taskID - task ID of event + * @param event - event value + * + * @return the timer's tick count if found, zero otherwise. + */ +extern tmosTimer tmos_get_task_timer( tmosTaskID taskID, tmosEvents event ); + +/** + * @brief send msg to a task,callback events&SYS_EVENT_MSG + * + * @param taskID - task ID of task need to send msg + * @param *msg_ptr - point of msg + * + * @return SUCCESS, INVALID_TASK, INVALID_MSG_POINTER + */ +extern bStatus_t tmos_msg_send( tmosTaskID taskID, uint8_t *msg_ptr ); + +/** + * @brief delete a msg + * + * @param *msg_ptr - point of msg + * + * @return SUCCESS. + */ +extern bStatus_t tmos_msg_deallocate( uint8_t *msg_ptr ); + +/** + * @brief receive a msg + * + * @param taskID - task ID of task need to receive msg + * + * @return *uint8_t - message information or NULL if no message + */ +extern uint8_t *tmos_msg_receive( tmosTaskID taskID ); + +/** + * @brief allocate buffer for msg when need to send msg + * + * @param len - length of msg + * + * @return pointer to allocated buffer or NULL if allocation failed. + */ +extern uint8_t *tmos_msg_allocate( uint16_t len ); + +/** + * @brief read a data item to NV. + * + * @param id - Valid NV item Id. + * @param len - Length of data to read. + * @param *pBuf - Data to read. + * + * @return SUCCESS if successful, NV_OPER_FAILED if failed. + */ +extern bStatus_t tmos_snv_read( tmosSnvId_t id, tmosSnvLen_t len, void *pBuf ); + +/** + * @brief tmos system timer initialization + * + * @note must initialization before call tmos task + * + * @param fnGetClock - 0:system clock select RTC timer + * valid:system clock select extend input + * + * @return SUCCESS if successful, FAILURE if failed. + */ +extern bStatus_t TMOS_TimerInit( pfnGetSysClock fnGetClock ); + +/** + * @brief interrupt handler. + * + * @param None + * + * @return None + */ +extern void TMOS_TimerIRQHandler( void ); + +/** + * @brief Process system + * + * @param None. + * + * @return None. + */ +extern void TMOS_SystemProcess( void ); + +/** + * @brief Get current system clock + * + * @param None. + * + * @return current system clock (in 0.625ms) + */ +extern uint32_t TMOS_GetSystemClock( void ); + +/** + * @brief register process event callback function + * + * @param eventCb-events callback function + * + * @return 0xFF - error,others-task id + */ +extern tmosTaskID TMOS_ProcessEventRegister( pTaskEventHandlerFn eventCb ); + +/** + * @brief Add a device address into white list ( support SNVNum MAX ) + * + * @param addrType - Type of device address + * @param devAddr - first address of device address + * + * @return Command Status. + */ +extern bStatus_t LL_AddWhiteListDevice( uint8_t addrType, uint8_t *devAddr ); + +/** + * @brief Remove a device address from white list + * + * @param addrType - Type of device address + * @param devAddr - first address of device address + * + * @return Command Status. + */ +extern bStatus_t LL_RemoveWhiteListDevice( uint8_t addrType, uint8_t *devAddr ); + +/** + * @brief Clear white list + * + * @param None + * + * @return Command Status. + */ +extern bStatus_t LL_ClearWhiteList( void ); + +/** + * @brief Encrypt data + * + * @param key - key + * @param plaintextData - original data + * @param encryptData - encrypted data + * + * @return Command Status. + */ +extern bStatus_t LL_Encrypt( uint8_t *key, uint8_t *plaintextData, uint8_t *encryptData ); + +/** + * @brief Decrypt data + * + * @param key - key + * @param plaintextData - original data + * @param decryptData - decrypted data + * + * @return Command Status. + */ +extern bStatus_t LL_Decrypt( uint8_t *key, uint8_t *plaintextData, uint8_t *decryptData ); + +/** + * @brief get number of unAck packet in current connect buffer + * + * @param handle - connect handle + * + * @return 0xFFFFFFFF-handle error,number of packets not receiving ack + */ +extern uint32_t LL_GetNumberOfUnAckPacket( uint16_t handle ); + +/** + * @brief Register a callback function will be called after each connect event. + * Only effect in single connection + * + * @param connEventCB - callback function + * + * @return None. + */ +extern void LL_ConnectEventRegister( pfnEventCB connEventCB ); + +/** + * @brief Register a callback function will be called after each advertise event. + * + * @param advEventCB - callback function + * + * @return None. + */ +extern void LL_AdvertiseEventRegister( pfnEventCB advEventCB ); + +/** + * @brief set tx power level + * + * @param power - tx power level + * + * @return Command Status. + */ +extern bStatus_t LL_SetTxPowerLevel( uint8_t power ); + +/** + * @brief read rssi + * + * @param None. + * + * @return the value of rssi. + */ +extern int8_t BLE_ReadRssi( void ); + +/** + * @brief read cfo + * + * @param None. + * + * @return the value of cfo. + */ +extern int16_t BLE_ReadCfo( void ); + +/** + * @brief pa control init + * + * @note Can't be called until role Init + * + * @param paControl - pa control parameters(global variable) + * + * @return Command Status. + */ +extern void BLE_PAControlInit( blePaControlConfig_t *paControl ); + +/** + * @brief ble register reset and rf calibration + * + * @param None + * + * @return None + */ +extern void BLE_RegInit( void ); + +/** + * @brief Init BLE lib. RTC will be occupied at the same time. + * + * @param pCfg - config of BLE lib + * + * @return 0-success. error defined @ ERR_LIB_INIT + */ +extern bStatus_t BLE_LibInit( bleConfig_t* pCfg ); + +/** + * @brief interrupt handler. + * + * @param None + * + * @return None + */ +extern void BB_IRQLibHandler( void ); + +/** + * @brief interrupt handler. + * + * @param None + * + * @return None + */ +extern void LLE_IRQLibHandler( void ); + +/** + * @brief generate a valid access address + * + * @param None. + * + * @return access address + * the Access Address meets the following requirements: + * It shall have no more than six consecutive zeros or ones. + * It shall not be t he advertising channel packets�� Access Address. + * It shall not be a sequence that differ s from the advertising channel packets' Access Address by only one bit. + * It shall not have all four octets equal. + * It shall have no more than 24 transitions. + * It shall have a minimum of two transitions in the most significant six bits. + */ +extern uint32_t BLE_AccessAddressGenerate( void ); + +/* + * linkDB_Register - Register with this function to receive a callback when + * status changes on a connection. + */ +extern uint8_t linkDB_Register( pfnLinkDBCB_t pFunc ); + +/* + * linkDB_State - Check to see if a physical link is in a specific state. + * + * returns TRUE is the link is in state. FALSE, otherwise. + */ +extern uint8_t linkDB_State( uint16_t connectionHandle, uint8_t state ); + +/* + * linkDB_PerformFunc - Perform a function of each connection in the link database. + */ +extern void linkDB_PerformFunc( pfnPerformFuncCB_t cb ); +/* + * linkDB_Up - Check to see if a physical link is up (connected). + * Use like: uint8_t linkDB_Up( uint16_t connectionHandle ); + * connectionHandle - controller link connection handle. + * returns TRUE if the link is up. FALSE, otherwise. + */ +#define linkDB_Up( connectionHandle ) linkDB_State( (connectionHandle), LINK_CONNECTED ) + +/** + * @brief This function is used to get the MTU size of a connection. + * + * @param connHandle - connection handle. + * + * @return connection MTU size.
+ */ +extern uint16_t ATT_GetMTU( uint16_t connHandle ); + +/** + * @brief Send Handle Value Confirmation. + * + * @param connHandle - connection to use + * + * @return SUCCESS: Confirmation was sent successfully.
+ * INVALIDPARAMETER: Invalid confirmation field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * bleMemAllocError: Memory allocation error occurred.
+ */ +extern bStatus_t ATT_HandleValueCfm( uint16_t connHandle ); + +/* + * Compare two UUIDs. The UUIDs are converted if necessary. + */ +extern uint8_t ATT_CompareUUID( const uint8_t *pUUID1, uint16_t len1, const uint8_t *pUUID2, uint16_t len2 ); + +/** + * @brief Initialize the Generic Attribute Profile Client. + * + * @return SUCCESS: Client initialized successfully.
+ * bleMemAllocError: Memory allocation error occurred.
+ */ +extern bStatus_t GATT_InitClient( void ); + +/** + * @brief Register to receive incoming ATT Indications or Notifications + * of attribute values. + * + * @param taskId ?task to forward indications or notifications to + * + * @return void + */ +extern void GATT_RegisterForInd( uint8_t taskId ); + +/** + * @brief Find the attribute record for a given handle + * + * @param handle - handle to look for + * @param pHandle - handle of owner of attribute (to be returned) + * + * @return Pointer to attribute record. NULL, otherwise. + */ +extern gattAttribute_t *GATT_FindHandle( uint16_t handle, uint16_t *pHandle ); + +/** + * @brief This sub-procedure is used when a server is configured to + * indicate a characteristic value to a client and expects an + * attribute protocol layer acknowledgement that the indication + * was successfully received. + * + * The ATT Handle Value Indication is used in this sub-procedure. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive an tmos GATT_MSG_EVENT message. + * The type of the message will be ATT_HANDLE_VALUE_CFM. + * + * @note This sub-procedure is complete when ATT_HANDLE_VALUE_CFM + * (with SUCCESS or bleTimeoutstatus) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pInd - pointer to indication to be sent + * @param authenticated - whether an authenticated link is required + * @param taskId - task to be notified of response + * + * @return SUCCESS: Indication was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A confirmation is pending with this client.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_Indication( uint16_t connHandle, attHandleValueInd_t *pInd, uint8_t authenticated, uint8_t taskId ); +/** + * @brief This sub-procedure is used when a server is configured to + * notify a characteristic value to a client without expecting + * any attribute protocol layer acknowledgement that the + * notification was successfully received. + * + * The ATT Handle Value Notification is used in this sub-procedure. + * + * @note A notification may be sent at any time and does not invoke a confirmation. + * No confirmation will be sent to the calling application task for + * this sub-procedure. + * + * @param connHandle - connection to use + * @param pNoti - pointer to notification to be sent + * @param authenticated - whether an authenticated link is required + * + * @return SUCCESS: Notification was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_Notification( uint16_t connHandle, attHandleValueNoti_t *pNoti, uint8_t authenticated ); + +/** + * @brief This sub-procedure is used by the client to set the ATT_MTU + * to the maximum possible value that can be supported by both + * devices when the client supports a value greater than the + * default ATT_MTU for the Attribute Protocol. This sub-procedure + * shall only be initiated once during a connection. + * + * The ATT Exchange MTU Request is used by this sub-procedure. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive an tmos GATT_MSG_EVENT message. + * The type of the message will be either ATT_EXCHANGE_MTU_RSP or + * ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_EXCHANGE_MTU_RSP + * (with SUCCESS or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_ExchangeMTU( uint16_t connHandle, attExchangeMTUReq_t *pReq, uint8_t taskId ); + +/** + * @brief This sub-procedure is used by a client to discover all + * the primary services on a server. + * + * The ATT Read By Group Type Request is used with the Attribute + * Type parameter set to the UUID for "Primary Service". The + * Starting Handle is set to 0x0001 and the Ending Handle is + * set to 0xFFFF. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive multiple tmos GATT_MSG_EVENT messages. + * The type of the messages will be either ATT_READ_BY_GRP_TYPE_RSP + * or ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_READ_BY_GRP_TYPE_RSP + * (with bleProcedureComplete or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_DiscAllPrimaryServices( uint16_t connHandle, uint8_t taskId ); + +/** + * @brief This sub-procedure is used by a client to discover a specific + * primary service on a server when only the Service UUID is + * known. The primary specific service may exist multiple times + * on a server. The primary service being discovered is identified + * by the service UUID. + * + * The ATT Find By Type Value Request is used with the Attribute + * Type parameter set to the UUID for "Primary Service" and the + * Attribute Value set to the 16-bit Bluetooth UUID or 128-bit + * UUID for the specific primary service. The Starting Handle shall + * be set to 0x0001 and the Ending Handle shall be set to 0xFFFF. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive multiple tmos GATT_MSG_EVENT messages. + * The type of the messages will be either ATT_FIND_BY_TYPE_VALUE_RSP + * or ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_FIND_BY_TYPE_VALUE_RSP + * (with bleProcedureComplete or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pUUID - pointer to service UUID to look for + * @param len - length of value + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_DiscPrimaryServiceByUUID( uint16_t connHandle, uint8_t *pUUID, uint8_t len, uint8_t taskId ); + +/** + * @brief This sub-procedure is used by a client to find include + * service declarations within a service definition on a + * server. The service specified is identified by the service + * handle range. + * + * The ATT Read By Type Request is used with the Attribute + * Type parameter set to the UUID for "Included Service". The + * Starting Handle is set to starting handle of the specified + * service and the Ending Handle is set to the ending handle + * of the specified service. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive multiple tmos GATT_MSG_EVENT messages. + * The type of the messages will be either ATT_READ_BY_TYPE_RSP + * or ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_READ_BY_TYPE_RSP + * (with bleProcedureCompleteor bleTimeout status)or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param startHandle - starting handle + * @param endHandle - end handle + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_FindIncludedServices( uint16_t connHandle, uint16_t startHandle, uint16_t endHandle, uint8_t taskId ); + +/** + * @brief This sub-procedure is used by a client to find all the + * characteristic declarations within a service definition on + * a server when only the service handle range is known. The + * service specified is identified by the service handle range. + * + * The ATT Read By Type Request is used with the Attribute Type + * parameter set to the UUID for "Characteristic". The Starting + * Handle is set to starting handle of the specified service and + * the Ending Handle is set to the ending handle of the specified + * service. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive multiple tmos GATT_MSG_EVENT messages. + * The type of the messages will be either ATT_READ_BY_TYPE_RSP + * or ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_READ_BY_TYPE_RSP + * (with bleProcedureComplete or bleTimeout status)or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param startHandle - starting handle + * @param endHandle - end handle + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_DiscAllChars( uint16_t connHandle, uint16_t startHandle, uint16_t endHandle, uint8_t taskId ); + +/** + * @brief This sub-procedure is used by a client to discover service + * characteristics on a server when only the service handle + * ranges are known and the characteristic UUID is known. + * The specific service may exist multiple times on a server. + * The characteristic being discovered is identified by the + * characteristic UUID. + * + * The ATT Read By Type Request is used with the Attribute Type + * is set to the UUID for "Characteristic" and the Starting + * Handle and Ending Handle parameters is set to the service + * handle range. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive multiple tmos GATT_MSG_EVENT messages. + * The type of the messages will be either ATT_READ_BY_TYPE_RSP + * or ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_READ_BY_TYPE_RSP + * (with bleProcedureComplete or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_DiscCharsByUUID( uint16_t connHandle, attReadByTypeReq_t *pReq, uint8_t taskId ); + +/** + * @brief This sub-procedure is used by a client to find all the + * characteristic descriptors Attribute Handles and Attribute + * Types within a characteristic definition when only the + * characteristic handle range is known. The characteristic + * specified is identified by the characteristic handle range. + * + * The ATT Find Information Request is used with the Starting + * Handle set to starting handle of the specified characteristic + * and the Ending Handle set to the ending handle of the specified + * characteristic. The UUID Filter parameter is NULL (zero length). + * + * If the return status from this function is SUCCESS, the calling + * application task will receive multiple tmos GATT_MSG_EVENT messages. + * The type of the messages will be either ATT_FIND_INFO_RSP or + * ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_FIND_INFO_RSP + * (with bleProcedureComplete or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param startHandle - starting handle + * @param endHandle - end handle + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_DiscAllCharDescs( uint16_t connHandle, uint16_t startHandle, uint16_t endHandle, uint8_t taskId ); + +/** + * @brief This sub-procedure is used to read a Characteristic Value + * from a server when the client knows the Characteristic Value + * Handle. The ATT Read Request is used with the Attribute Handle + * parameter set to the Characteristic Value Handle. The Read + * Response returns the Characteristic Value in the Attribute + * Value parameter. + * + * The Read Response only contains a Characteristic Value that + * is less than or equal to (ATT_MTU ?1) octets in length. If + * the Characteristic Value is greater than (ATT_MTU - 1) octets + * in length, the Read Long Characteristic Value procedure may + * be used if the rest of the Characteristic Value is required. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive an tmos GATT_MSG_EVENT message. + * The type of the message will be either ATT_READ_RSP or + * ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_READ_RSP + * (with SUCCESS or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_ReadCharValue( uint16_t connHandle, attReadReq_t *pReq, uint8_t taskId ); + +/** + * @brief This sub-procedure is used to read a Characteristic Value + * from a server when the client only knows the characteristic + * UUID and does not know the handle of the characteristic. + * + * The ATT Read By Type Request is used to perform the sub-procedure. + * The Attribute Type is set to the known characteristic UUID and + * the Starting Handle and Ending Handle parameters shall be set + * to the range over which this read is to be performed. This is + * typically the handle range for the service in which the + * characteristic belongs. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive an tmos GATT_MSG_EVENT messages. + * The type of the message will be either ATT_READ_BY_TYPE_RSP + * or ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_READ_BY_TYPE_RSP + * (with SUCCESS or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_ReadUsingCharUUID( uint16_t connHandle, attReadByTypeReq_t *pReq, uint8_t taskId ); + +/** + * @brief This sub-procedure is used to read a Characteristic Value from + * a server when the client knows the Characteristic Value Handle + * and the length of the Characteristic Value is longer than can + * be sent in a single Read Response Attribute Protocol message. + * + * The ATT Read Blob Request is used in this sub-procedure. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive multiple tmos GATT_MSG_EVENT messages. + * The type of the messages will be either ATT_READ_BLOB_RSP or + * ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_READ_BLOB_RSP + * (with bleProcedureComplete or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_ReadLongCharValue( uint16_t connHandle, attReadBlobReq_t *pReq, uint8_t taskId ); + +/** + * @brief This sub-procedure is used to read multiple Characteristic Values + * from a server when the client knows the Characteristic Value + * Handles. The Attribute Protocol Read Multiple Requests is used + * with the Set Of Handles parameter set to the Characteristic Value + * Handles. The Read Multiple Response returns the Characteristic + * Values in the Set Of Values parameter. + * + * The ATT Read Multiple Request is used in this sub-procedure. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive an tmos GATT_MSG_EVENT message. + * The type of the message will be either ATT_READ_MULTI_RSP + * or ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_READ_MULTI_RSP + * (with SUCCESS or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_ReadMultiCharValues( uint16_t connHandle, attReadMultiReq_t *pReq, uint8_t taskId ); + +/** + * @brief This sub-procedure is used to write a Characteristic Value + * to a server when the client knows the Characteristic Value + * Handle and the client does not need an acknowledgement that + * the write was successfully performed. This sub-procedure + * only writes the first (ATT_MTU ?3) octets of a Characteristic + * Value. This sub-procedure can not be used to write a long + * characteristic; instead the Write Long Characteristic Values + * sub-procedure should be used. + * + * The ATT Write Command is used for this sub-procedure. The + * Attribute Handle parameter shall be set to the Characteristic + * Value Handle. The Attribute Value parameter shall be set to + * the new Characteristic Value. + * + * No response will be sent to the calling application task for this + * sub-procedure. If the Characteristic Value write request is the + * wrong size, or has an invalid value as defined by the profile, + * then the write will not succeed and no error will be generated + * by the server. + * + * @param connHandle - connection to use + * @param pReq - pointer to command to be sent + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_WriteNoRsp( uint16_t connHandle, attWriteReq_t *pReq ); + +/** + * @brief This sub-procedure is used to write a Characteristic Value + * to a server when the client knows the Characteristic Value + * Handle and the ATT Bearer is not encrypted. This sub-procedure + * shall only be used if the Characteristic Properties authenticated + * bit is enabled and the client and server device share a bond as + * defined in the GAP. + * + * This sub-procedure only writes the first (ATT_MTU ?15) octets + * of an Attribute Value. This sub-procedure cannot be used to + * write a long Attribute. + * + * The ATT Write Command is used for this sub-procedure. The + * Attribute Handle parameter shall be set to the Characteristic + * Value Handle. The Attribute Value parameter shall be set to + * the new Characteristic Value authenticated by signing the + * value, as defined in the Security Manager. + * + * No response will be sent to the calling application task for this + * sub-procedure. If the authenticated Characteristic Value that is + * written is the wrong size, or has an invalid value as defined by + * the profile, or the signed value does not authenticate the client, + * then the write will not succeed and no error will be generated by + * the server. + * + * @param connHandle - connection to use + * @param pReq - pointer to command to be sent + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleLinkEncrypted: Connection is already encrypted.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_SignedWriteNoRsp( uint16_t connHandle, attWriteReq_t *pReq ); + +/** + * @brief This sub-procedure is used to write a characteristic value + * to a server when the client knows the characteristic value + * handle. This sub-procedure only writes the first (ATT_MTU-3) + * octets of a characteristic value. This sub-procedure can not + * be used to write a long attribute; instead the Write Long + * Characteristic Values sub-procedure should be used. + * + * The ATT Write Request is used in this sub-procedure. The + * Attribute Handle parameter shall be set to the Characteristic + * Value Handle. The Attribute Value parameter shall be set to + * the new characteristic. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive an tmos GATT_MSG_EVENT message. + * The type of the message will be either ATT_WRITE_RSP + * or ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_WRITE_RSP + * (with SUCCESS or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_WriteCharValue( uint16_t connHandle, attWriteReq_t *pReq, uint8_t taskId ); + +/** + * @brief This sub-procedure is used to write a Characteristic Value to + * a server when the client knows the Characteristic Value Handle + * but the length of the Characteristic Value is longer than can + * be sent in a single Write Request Attribute Protocol message. + * + * The ATT Prepare Write Request and Execute Write Request are + * used to perform this sub-procedure. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive multiple tmos GATT_MSG_EVENT messages. + * The type of the messages will be either ATT_PREPARE_WRITE_RSP, + * ATT_EXECUTE_WRITE_RSP or ATT_ERROR_RSP (if an error occurred on + * the server). + * + * @note This sub-procedure is complete when either ATT_PREPARE_WRITE_RSP + * (with bleTimeout status), ATT_EXECUTE_WRITE_RSP + * (with SUCCESS or bleTimeout status), or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @note The 'pReq->pValue' pointer will be freed when the sub-procedure is complete. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_WriteLongCharValue( uint16_t connHandle, attPrepareWriteReq_t *pReq, uint8_t taskId ); + +/** + * @brief This sub-procedure is used to write a Characteristic Value to + * a server when the client knows the Characteristic Value Handle, + * and assurance is required that the correct Characteristic Value + * is going to be written by transferring the Characteristic Value + * to be written in both directions before the write is performed. + * This sub-procedure can also be used when multiple values must + * be written, in order, in a single operation. + * + * The sub-procedure has two phases, the first phase prepares the + * characteristic values to be written. Once this is complete, + * the second phase performs the execution of all of the prepared + * characteristic value writes on the server from this client. + * + * In the first phase, the ATT Prepare Write Request is used. + * In the second phase, the attribute protocol Execute Write + * Request is used. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive multiple tmos GATT_MSG_EVENT messages. + * The type of the messages will be either ATT_PREPARE_WRITE_RSP, + * ATT_EXECUTE_WRITE_RSP or ATT_ERROR_RSP (if an error occurred on + * the server). + * + * @note This sub-procedure is complete when either ATT_PREPARE_WRITE_RSP + * (with bleTimeout status), ATT_EXECUTE_WRITE_RSP + * (with SUCCESS or bleTimeout status), or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @note The 'pReqs' pointer will be freed when the sub-procedure is complete. + * + * @param connHandle - connection to use + * @param pReqs - pointer to requests to be sent + * @param numReqs - number of requests in pReq + * @param flags - execute write request flags + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_ReliableWrites( uint16_t connHandle, attPrepareWriteReq_t *pReqs, uint8_t numReqs, + uint8_t flags, uint8_t taskId ); + +/** + * @brief This sub-procedure is used to read a characteristic descriptor + * from a server when the client knows the characteristic descriptor + * declaration's Attribute handle. + * + * The ATT Read Request is used for this sub-procedure. The Read + * Request is used with the Attribute Handle parameter set to the + * characteristic descriptor handle. The Read Response returns the + * characteristic descriptor value in the Attribute Value parameter. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive an tmos GATT_MSG_EVENT message. + * The type of the message will be either ATT_READ_RSP or + * ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_READ_RSP + * (with SUCCESS or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_ReadCharDesc( uint16_t connHandle, attReadReq_t *pReq, uint8_t taskId ); + +/** + * @brief This sub-procedure is used to read a characteristic descriptor + * from a server when the client knows the characteristic descriptor + * declaration's Attribute handle and the length of the characteristic + * descriptor declaration is longer than can be sent in a single Read + * Response attribute protocol message. + * + * The ATT Read Blob Request is used to perform this sub-procedure. + * The Attribute Handle parameter shall be set to the characteristic + * descriptor handle. The Value Offset parameter shall be the offset + * within the characteristic descriptor to be read. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive multiple tmos GATT_MSG_EVENT messages. + * The type of the messages will be either ATT_READ_BLOB_RSP or + * ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_READ_BLOB_RSP + * (with bleProcedureComplete or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_ReadLongCharDesc( uint16_t connHandle, attReadBlobReq_t *pReq, uint8_t taskId ); + +/** + * @brief This sub-procedure is used to write a characteristic + * descriptor value to a server when the client knows the + * characteristic descriptor handle. + * + * The ATT Write Request is used for this sub-procedure. The + * Attribute Handle parameter shall be set to the characteristic + * descriptor handle. The Attribute Value parameter shall be + * set to the new characteristic descriptor value. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive an tmos GATT_MSG_EVENT message. + * The type of the message will be either ATT_WRITE_RSP + * or ATT_ERROR_RSP (if an error occurred on the server). + * + * @note This sub-procedure is complete when either ATT_WRITE_RSP + * (with SUCCESS or bleTimeout status) or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_WriteCharDesc( uint16_t connHandle, attWriteReq_t *pReq, uint8_t taskId ); + +/** + * @brief This sub-procedure is used to write a Characteristic Value to + * a server when the client knows the Characteristic Value Handle + * but the length of the Characteristic Value is longer than can + * be sent in a single Write Request Attribute Protocol message. + * + * The ATT Prepare Write Request and Execute Write Request are + * used to perform this sub-procedure. + * + * If the return status from this function is SUCCESS, the calling + * application task will receive multiple tmos GATT_MSG_EVENT messages. + * The type of the messages will be either ATT_PREPARE_WRITE_RSP, + * ATT_EXECUTE_WRITE_RSP or ATT_ERROR_RSP (if an error occurred on + * the server). + * + * @note This sub-procedure is complete when either ATT_PREPARE_WRITE_RSP + * (with bleTimeout status), ATT_EXECUTE_WRITE_RSP + * (with SUCCESS or bleTimeout status), or ATT_ERROR_RSP + * (with SUCCESS status) is received by the calling application task. + * + * @note The 'pReq->pValue' pointer will be freed when the sub-procedure is complete. + * + * @param connHandle - connection to use + * @param pReq - pointer to request to be sent + * @param taskId - task to be notified of response + * + * @return SUCCESS: Request was sent successfully.
+ * INVALIDPARAMETER: Invalid connection handle or request field.v + * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A response is pending with this server.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleTimeout: Previous transaction timed out.
+ */ +extern bStatus_t GATT_WriteLongCharDesc( uint16_t connHandle, attPrepareWriteReq_t *pReq, uint8_t taskId ); + +/** + * @brief GATT implementation of the allocator functionality. + * + * @note This function should only be called by GATT and the upper layer protocol/application. + * + * @param connHandle - connection that message is to be sent on. + * @param opcode - opcode of message that buffer to be allocated for. + * @param size - number of bytes to allocate from the heap. + * @param pSizeAlloc - number of bytes allocated for the caller from the heap. + * @param flag - . + * + * @return pointer to the heap allocation; NULL if error or failure. + */ +extern void *GATT_bm_alloc( uint16_t connHandle, uint8_t opcode, uint16_t size, uint16_t *pSizeAlloc, uint8_t flag ); + +/** + * @brief GATT implementation of the de-allocator functionality. + * + * @param pMsg - pointer to GATT message containing the memory to free. + * @param opcode - opcode of the message + * + * @return none + */ +extern void GATT_bm_free( gattMsg_t *pMsg, uint8_t opcode ); + +/** + * @brief Register a service's attribute list and callback functions with + * the GATT Server Application. + * + * @param pAttrs - Array of attribute records to be registered + * @param numAttrs - Number of attributes in array + * @param encKeySize - Minimum encryption key size required by service (7-16 bytes) + * @param pServiceCBs - Service callback function pointers + * + * @return SUCCESS: Service registered successfully.
+ * INVALIDPARAMETER: Invalid service fields.
+ * FAILURE: Not enough attribute handles available.
+ * bleMemAllocError: Memory allocation error occurred.
+ * bleInvalidRange: Encryption key size's out of range.
+ */ +extern bStatus_t GATTServApp_RegisterService( gattAttribute_t *pAttrs, uint16_t numAttrs, + uint8_t encKeySize, gattServiceCBs_t *pServiceCBs ); + +/** + * @brief Add function for the GATT Service. + * + * @param services - services to add. This is a bit map and can + * contain more than one service. + * + * @return SUCCESS: Service added successfully.
+ * INVALIDPARAMETER: Invalid service field.
+ * FAILURE: Not enough attribute handles available.
+ * bleMemAllocError: Memory allocation error occurred.
+ */ +extern bStatus_t GATTServApp_AddService( uint32_t services ); + +/** + * @brief Deregister a service's attribute list and callback functions from + * the GATT Server Application. + * + * @note It's the caller's responsibility to free the service attribute + * list returned from this API. + * + * @param handle - handle of service to be deregistered + * @param p2pAttrs - pointer to array of attribute records (to be returned) + * + * @return SUCCESS: Service deregistered successfully.
+ * FAILURE: Service not found.
+ */ +extern bStatus_t GATTServApp_DeregisterService( uint16_t handle, gattAttribute_t **p2pAttrs ); + +/** + * @brief Initialize the client characteristic configuration table. + * + * @note Each client has its own instantiation of the ClientCharacteristic Configuration. + * Reads/Writes of the Client Characteristic Configuration only only affect the + * configuration of that client. + * + * @param connHandle - connection handle (0xFFFF for all connections). + * @param charCfgTbl - client characteristic configuration table. + * + * @return none + */ +extern void GATTServApp_InitCharCfg( uint16_t connHandle, gattCharCfg_t *charCfgTbl ); + +/** + * @brief Send out a Service Changed Indication. + * + * @param connHandle - connection to use + * @param taskId - task to be notified of confirmation + * + * @return SUCCESS: Indication was sent successfully.
+ * FAILURE: Service Changed attribute not found.
+ * INVALIDPARAMETER: Invalid connection handle or request field.
+ * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
+ * bleNotConnected: Connection is down.
+ * blePending: A confirmation is pending with this client.
+ */ +extern bStatus_t GATTServApp_SendServiceChangedInd( uint16_t connHandle, uint8_t taskId ); + +/** + * @brief Read the client characteristic configuration for a given client. + * + * @note Each client has its own instantiation of the Client Characteristic Configuration. + * Reads of the Client Characteristic Configuration only shows the configuration + * for that client. + * + * @param connHandle - connection handle. + * @param charCfgTbl - client characteristic configuration table. + * + * @return attribute value + */ +extern uint16_t GATTServApp_ReadCharCfg( uint16_t connHandle, gattCharCfg_t *charCfgTbl ); + +/** + * @brief Write the client characteristic configuration for a given client. + * + * @note Each client has its own instantiation of the Client Characteristic Configuration. + * Writes of the Client Characteristic Configuration only only affect the + * configuration of that client. + * + * @param connHandle - connection handle. + * @param charCfgTbl - client characteristic configuration table. + * @param value - attribute new value. + * + * @return Success or Failure + */ +extern uint8_t GATTServApp_WriteCharCfg( uint16_t connHandle, gattCharCfg_t *charCfgTbl, uint16_t value ); + +/** + * @brief Process the client characteristic configuration + * write request for a given client. + * + * @param connHandle - connection message was received on. + * @param pAttr - pointer to attribute. + * @param pValue - pointer to data to be written. + * @param len - length of data. + * @param offset - offset of the first octet to be written. + * @param validCfg - valid configuration. + * + * @return Success or Failure + */ +extern bStatus_t GATTServApp_ProcessCCCWriteReq( uint16_t connHandle, gattAttribute_t *pAttr, + uint8_t *pValue, uint16_t len, uint16_t offset, uint16_t validCfg ); + +/** + * @brief Set a GAP GATT Server parameter. + * + * @param param - Profile parameter ID
+ * @param len - length of data to right + * @param value - pointer to data to write. This is dependent on + * the parameter ID and WILL be cast to the appropriate + * data type (example: data type of uint16_t will be cast to + * uint16_t pointer).
+ * + * @return bStatus_t + */ +extern bStatus_t GGS_SetParameter( uint8_t param, uint8_t len, void *value ); + +/** + * @brief Get a GAP GATT Server parameter. + * + * @param param - Profile parameter ID
+ * @param value - pointer to data to put. This is dependent on + * the parameter ID and WILL be cast to the appropriate + * data type (example: data type of uint16_t will be cast to + * uint16_t pointer).
+ * + * @return bStatus_t + */ +extern bStatus_t GGS_GetParameter( uint8_t param, void *value ); + +/** + * @brief Add function for the GAP GATT Service. + * + * @param services - services to add. This is a bit map and can + * contain more than one service. + * + * @return SUCCESS: Service added successfully.
+ * INVALIDPARAMETER: Invalid service field.
+ * FAILURE: Not enough attribute handles available.
+ * bleMemAllocError: Memory allocation error occurred.
+ */ +extern bStatus_t GGS_AddService( uint32_t services ); + +/*------------------------------------------------------------------- + * FUNCTIONS - Initialization and Configuration + */ + +/** + * @brief Set a GAP Parameter value. Use this function to change the default GAP parameter values. + * + * @param paramID - parameter ID: @ref GAP_PARAMETER_ID_DEFINES + * @param paramValue - new param value + * + * @return SUCCESS or INVALIDPARAMETER (invalid paramID) + */ +extern bStatus_t GAP_SetParamValue( uint16_t paramID, uint16_t paramValue ); + +/** + * @brief Get a GAP Parameter value. + * + * @note This function is the same as GAP_PasskeyUpdate(), except that + * the passkey is passed in as a non-string format. + * + * @param paramID - parameter ID: @ref GAP_PARAMETER_ID_DEFINES + * + * @return GAP Parameter value or 0xFFFF if invalid + */ +extern uint16_t GAP_GetParamValue( uint16_t paramID ); + +/** + * @brief Setup the device's address type. If ADDRTYPE_PRIVATE_RESOLVE is selected, + * the address will change periodically. + * + * @param addrType - @ref GAP_ADDR_TYPE_DEFINES + * @param pStaticAddr - Only used with ADDRTYPE_STATIC or ADDRTYPE_PRIVATE_NONRESOLVE type + * NULL to auto generate otherwise the application can specify the address value + * + * @return SUCCESS: address type updated,
+ * bleNotReady: Can't be called until GAP_DeviceInit() is called + * and the init process is completed + * bleIncorrectMode: can't change with an active connection,or INVALIDPARAMETER + * If return value isn't SUCCESS, the address type remains the same as before this call. + */ +extern bStatus_t GAP_ConfigDeviceAddr( uint8_t addrType, uint8_t *pStaticAddr ); + +/** + * @brief Resolves a private address against an IRK. + * + * @param(in) pIRK - pointer to the IRK + * @param(in) pAddr - pointer to the Resolvable Private address + * + * @param(out) pIRK + * @param(out) pAddr + * + * @return SUCCESS: match,
+ * FAILURE: don't match,
+ * INVALIDPARAMETER: parameters invalid
+ */ +extern bStatus_t GAP_ResolvePrivateAddr( uint8_t *pIRK, uint8_t *pAddr ); + +/** + * @brief Setup or change advertising and scan response data. + * + * @note if the return status from this function is SUCCESS,the task isn't complete + * until the GAP_ADV_DATA_UPDATE_DONE_EVENT is sent to the calling application task. + * + * @param taskID - task ID of the app requesting the change + * @param adType - TRUE - advertisement data, FALSE - scan response data + * @param dataLen - Octet length of advertData + * @param pAdvertData - advertising or scan response data + * + * @return SUCCESS: data accepted + * bleIncorrectMode: invalid profile role + */ +extern bStatus_t GAP_UpdateAdvertisingData( uint8_t taskID, uint8_t adType, uint16_t dataLen, uint8_t *pAdvertData ); + +/*------------------------------------------------------------------- + * FUNCTIONS - GAP Bond API + */ +/** + * @brief Set a GAP Bond Manager parameter. + * + * @note You can call this function with a GAP Parameter ID and it will set the GAP Parameter. + * + * @param param - Profile parameter ID: @ref GAPBOND_PROFILE_PARAMETERS + * @param len - length of data to write + * @param pValue - pointer to data to write. This is dependent on + * the parameter ID and WILL be cast to the appropriate + * data type (example: data type of uint16_t will be cast to + * uint16_t pointer). + * + * @return SUCCESS or INVALIDPARAMETER (invalid paramID) + */ +extern bStatus_t GAPBondMgr_SetParameter( uint16_t param, uint8_t len, void *pValue ); + +/** + * @brief Get a GAP Bond Manager parameter. + * + * @note You can call this function with a GAP Parameter ID and it will get a GAP Parameter. + * + * @param param - Profile parameter ID: @ref GAPBOND_PROFILE_PARAMETERS + * @param pValue - pointer to location to get the value. This is dependent on + * the parameter ID and WILL be cast to the appropriate data type. + * (example: data type of uint16_t will be cast to uint16_t pointer) + * + * @return SUCCESS or INVALIDPARAMETER (invalid paramID) + */ +extern bStatus_t GAPBondMgr_GetParameter( uint16_t param, void *pValue ); + +/** + * @brief Respond to a passcode request. + * + * @param connectionHandle - connection handle of the connected device or 0xFFFF if all devices in database. + * @param status - SUCCESS if passcode is available, otherwise see @ref SMP_PAIRING_FAILED_DEFINES. + * @param passcode - integer value containing the passcode. + * + * @return SUCCESS - bond record found and changed + * bleIncorrectMode - Link not found. + */ +extern bStatus_t GAPBondMgr_PasscodeRsp( uint16_t connectionHandle, uint8_t status, uint32_t passcode ); + +/** + * @brief Respond to a passcode request. + * + * @param connHandle - connection handle of the connected device or 0xFFFF if all devices in database. + * @param status - SUCCESS if oob data is available, otherwise see @ref SMP_PAIRING_FAILED_DEFINES. + * @param oob - containing the oob data. + * @param c_peer - containing the peer confirm. + * + * @return SUCCESS - bond record found and changed + * bleIncorrectMode - Link not found. + */ +extern bStatus_t GAPBondMgr_OobRsp( uint16_t connHandle, uint8_t status, uint8_t *oob, uint8_t * c_peer ); + +/** + * @brief Initialization function for the ecc-function callback. + * + * @param pEcc - callback registration Structure @ref gapEccCBs_t. + * + * @return null. + */ +extern void GAPBondMgr_EccInit( gapEccCBs_t *pEcc ); + +/** + * @brief Send a security request + * + * @param connHandle - connection handle + * + * @return SUCCESS: will send + * bleNotConnected: Link not found + * bleIncorrectMode: wrong GAP role, must be a Peripheral Role + */ +extern bStatus_t GAPBondMgr_PeriSecurityReq( uint16_t connHandle ); + +/*------------------------------------------------------------------- + * FUNCTIONS - GAPRole API + */ +/** + * @brief Set a GAP Role parameter. + * + * @note You can call this function with a GAP Parameter ID and it will set a GAP Parameter. + * + * @param param - Profile parameter ID: @ref GAPROLE_PROFILE_PARAMETERS + * @param len - length of data to write + * @param pValue - pointer to data to write. This is dependent on the parameter ID and + * WILL be cast to the appropriate data type (example: data type of uint16_t + * will be cast to uint16_t pointer). + * + * @return SUCCESS or INVALIDPARAMETER (invalid paramID) + */ +extern bStatus_t GAPRole_SetParameter( uint16_t param, uint16_t len, void *pValue ); + +/** + * @brief Get a GAP Role parameter. + * + * @note You can call this function with a GAP Parameter ID and it will get a GAP Parameter. + * + * @param param - Profile parameter ID: @ref GAPROLE_PROFILE_PARAMETERS + * @param pValue - pointer to location to get the value. This is dependent on + * the parameter ID and WILL be cast to the appropriate + * data type (example: data type of uint16_t will be cast to + * uint16_t pointer). + * + * @return SUCCESS or INVALIDPARAMETER (invalid paramID) + */ +extern bStatus_t GAPRole_GetParameter( uint16_t param, void *pValue ); + +/** + * @brief Terminates the existing connection. + * + * @return SUCCESS or bleIncorrectMode + */ +extern bStatus_t GAPRole_TerminateLink( uint16_t connHandle ); + +/** + * @brief Read Rssi Cmd. + * + * @param connHandle - connection handle + * + * @return bStatus_t: HCI Error Code.
+ * + */ +extern bStatus_t GAPRole_ReadRssiCmd( uint16_t connHandle ); + +/** + * @brief used to synchronize with a periodic advertising train from an advertiser and + * begin receiving periodic advertising packets. + * + * @param pSync - sync parameters@ gapCreateSync_t + * + * @return bStatus_t: HCI Error Code.
+ * + */ +extern bStatus_t GAPRole_CreateSync( gapCreateSync_t *pSync ); + +/** + * @brief used to cancel the HCI_LE_Periodic_Advertising_Create_Sync command while + * it is pending. + * + * @param None. + * + * @return bStatus_t: HCI Error Code.
+ * + */ +extern bStatus_t GAPRole_CancelSync( void ); + +/** + * @brief used to stop reception of the periodic advertising train identified + * by the Sync_Handle parameter. + * + * @param syncHandle-identifying the periodic advertising train + * + * @return bStatus_t: HCI Error Code.
+ * + */ +extern bStatus_t GAPRole_TerminateSync( uint16_t syncHandle ); + +/** + * @brief Update the link connection parameters. + * + * @param connHandle - connection handle + * @param connIntervalMin - minimum connection interval in 1.25ms units + * @param connIntervalMax - maximum connection interval in 1.25ms units + * @param connLatency - number of LL latency connection events + * @param connTimeout - connection timeout in 10ms units + * + * @return SUCCESS: Connection update started started.
+ * bleIncorrectMode: No connection to update.
+ */ +extern bStatus_t GAPRole_UpdateLink( uint16_t connHandle, uint16_t connIntervalMin, + uint16_t connIntervalMax, uint16_t connLatency, uint16_t connTimeout ); + +/** + * @brief Update the connection phy. + * + * @param connHandle - connection handle + * @param all_phys - a bit field that allows the Host to specify, for each direction + * set BIT0:The Host has no preference among the transmitter PHYs supported by the Controller + * set BIT1:The Host has no preference among the receiver PHYs supported by the Controller + * @param tx_phys - a bit field that indicates the transmitter PHYs.(GAP_PHY_BIT_TYPE) + * @param rx_phys - a bit field that indicates the receiver PHYs.(GAP_PHY_BIT_TYPE) + * @param phy_options - preferred coding when transmitting on the LE Coded PHY(GAP_PHY_OPTIONS_TYPE) + * + * @return SUCCESS: PHY update started started .
+ * bleIncorrectMode: No connection to update.
+ */ +extern bStatus_t GAPRole_UpdatePHY( uint16_t connHandle, uint8_t all_phys, uint8_t tx_phys,\ + uint8_t rx_phys, uint16_t phy_options ); + +/** + * @brief used to allow the Host to specify the privacy mode to be used for a given entry on the resolving list. + * + * @note This command shall not be used when address resolution is enabled in the Controller and: + * Advertising (other than periodic advertising) is enabled, + * Scanning is enabled, or + * an GAPRole_CentralEstablishLink, or GAPRole_CreateSync command is pending. + * + * @param addrTypePeer - 0x00:Public Identity Address 0x01:Random (static) Identity Address + * @param peerAddr - Public Identity Address or Random (static) Identity Address of the advertiser + * @param privacyMode - 0x00:Use Network Privacy Mode for this peer device (default) + * 0x01:Use Device Privacy Mode for this peer device + * + * @return Command Status. + * + */ +extern bStatus_t GAPRole_SetPrivacyMode( uint8_t addrTypePeer, uint8_t *peerAddr, uint8_t privacyMode ); + +/** + * @brief used to set the path loss threshold reporting parameters. + * + * @param pParm - set path loss parameters@ gapRoleSetPathLossReporting_t + * + * @return Command Status. + * + */ +extern bStatus_t GAPRole_SetPathLossReporting( gapRoleSetPathLossReporting_t *pParm ); + +/** + * @brief used to set power level management. + * + * @param pParm - set power level parameters@ gapRolePowerlevelManagement_t + * + * @return Command Status. + * + */ +extern bStatus_t GAPRole_SetPowerlevel( gapRolePowerlevelManagement_t *pParm ); + +/*------------------------------------------------------------------- + * FUNCTIONS - BROADCASTER_PROFILE_API Broadcaster Profile API + */ +/** + * + * @brief Initialization function for the GAP Role Task. + * + * @param None. + * + * @return SUCCESS,bleInvalidRange + */ +extern bStatus_t GAPRole_BroadcasterInit( void ); + +/** + * @brief Does the device initialization. Only call this function once. + * + * @param pAppCallbacks - pointer to application callbacks. + * + * @return SUCCESS or bleAlreadyInRequestedMode + */ +extern bStatus_t GAPRole_BroadcasterStartDevice( gapRolesBroadcasterCBs_t *pAppCallbacks ); + +/** + * @brief Does the Broadcaster receive scan request call initialization. + * + * @param pAppCallbacks - pointer to application callbacks. + * + * @return None + */ +extern void GAPRole_BroadcasterSetCB( gapRolesBroadcasterCBs_t *pAppCallbacks ); + +/*------------------------------------------------------------------- + * FUNCTIONS - OBSERVER_PROFILE_API Observer Profile API + */ +/** + * @internal + * + * @brief Observer Profile Task initialization function. + * + * @param None. + * + * @return SUCCESS,bleInvalidRange + */ +extern bStatus_t GAPRole_ObserverInit( void ); + +/** + * @brief Start the device in Observer role. This function is typically + * called once during system startup. + * + * @param pAppCallbacks - pointer to application callbacks + * + * @return SUCCESS: Operation successful.
+ * bleAlreadyInRequestedMode: Device already started.
+ */ +extern bStatus_t GAPRole_ObserverStartDevice( gapRoleObserverCB_t *pAppCallbacks ); + +/** + * @brief Start a device discovery scan. + * + * @param mode - discovery mode: @ref GAP_DEVDISC_MODE_DEFINES + * @param activeScan - TRUE to perform active scan + * @param whiteList - TRUE to only scan for devices in the white list + * + * @return SUCCESS: Discovery scan started.
+ * bleIncorrectMode: Invalid profile role.
+ * bleAlreadyInRequestedMode: Not available.
+ */ +extern bStatus_t GAPRole_ObserverStartDiscovery( uint8_t mode, uint8_t activeScan, uint8_t whiteList ); + +/** + * @brief Cancel a device discovery scan. + * + * @return SUCCESS: Cancel started.
+ * bleInvalidTaskID: Not the task that started discovery.
+ * bleIncorrectMode: Not in discovery mode.
+ */ +extern bStatus_t GAPRole_ObserverCancelDiscovery( void ); + +/*------------------------------------------------------------------- + * FUNCTIONS - PERIPHERAL_PROFILE_API Peripheral Profile API + */ +/** + * @internal + * + * @brief Initialization function for the GAP Role Task. + * This is called during initialization and should contain + * any application specific initialization (ie. hardware + * initialization/setup, table initialization, power up + * notificaiton ... ). + * + * @param None. + * + * @return SUCCESS,bleInvalidRange + */ +extern bStatus_t GAPRole_PeripheralInit( void ); + +/** + * @brief Does the device initialization. Only call this function once. + * + * @param pAppCallbacks - pointer to application callbacks. + * + * @return SUCCESS or bleAlreadyInRequestedMode + */ +extern bStatus_t GAPRole_PeripheralStartDevice( uint8_t taskid, gapBondCBs_t *pCB, gapRolesCBs_t *pAppCallbacks ); + +/** + * @brief Update the parameters of an existing connection + * + * @param connHandle - the connection Handle + * @param connIntervalMin - minimum connection interval in 1.25ms units + * @param connIntervalMax - maximum connection interval in 1.25ms units + * @param latency - the new slave latency + * @param connTimeout - the new timeout value + * @param taskId - taskID will recv L2CAP_SIGNAL_EVENT message + * + * @return SUCCESS, bleNotConnected or bleInvalidRange + */ +extern bStatus_t GAPRole_PeripheralConnParamUpdateReq( uint16_t connHandle, uint16_t connIntervalMin, + uint16_t connIntervalMax, uint16_t latency, uint16_t connTimeout, uint8_t taskId ); + +/*------------------------------------------------------------------- + * FUNCTIONS - CENTRAL_PROFILE_API Central Profile API + */ +/** + * @internal + * + * @brief Central Profile Task initialization function. + * + * @param None. + * + * @return SUCCESS,bleInvalidRange + */ +extern bStatus_t GAPRole_CentralInit( void ); + +/** + * @brief Start the device in Central role. This function is typically + * called once during system startup. + * + * @param pAppCallbacks - pointer to application callbacks + * + * @return SUCCESS: Operation successful.
+ * bleAlreadyInRequestedMode: Device already started.
+ */ +extern bStatus_t GAPRole_CentralStartDevice( uint8_t taskid, gapBondCBs_t *pCB, gapCentralRoleCB_t *pAppCallbacks ); + +/** + * @brief Start a device discovery scan. + * + * @param mode - discovery mode: @ref GAP_DEVDISC_MODE_DEFINES + * @param activeScan - TRUE to perform active scan + * @param whiteList - TRUE to only scan for devices in the white list + * + * @return SUCCESS: Discovery scan started.
+ * bleIncorrectMode: Invalid profile role.
+ * bleAlreadyInRequestedMode: Not available.
+ */ +extern bStatus_t GAPRole_CentralStartDiscovery( uint8_t mode, uint8_t activeScan, uint8_t whiteList ); + +/** + * @brief Cancel a device discovery scan. + * + * @return SUCCESS: Cancel started.
+ * bleInvalidTaskID: Not the task that started discovery.
+ * bleIncorrectMode: Not in discovery mode.
+ */ +extern bStatus_t GAPRole_CentralCancelDiscovery( void ); + +/** + * @brief This API is called by the Central to update the Host data channels + * initiating an Update Data Channel control procedure. + * + * @note While it isn't specified,it is assumed that the Host expects an + * update channel map on all active connections and periodic advertise. + * + * input parameters + * + * @param chanMap - A five byte array containing one bit per data channel + * where a 1 means the channel is "used". + * + * @return SUCCESS + */ +extern void GAPRole_SetHostChanClassification( uint8_t *chanMap ); + +/** + * @brief Establish a link to a peer device. + * + * @param highDutyCycle - TRUE to high duty cycle scan, FALSE if not + * @param whiteList - determines use of the white list: TRUE-enable + * @param addrTypePeer - address type of the peer device: @ref GAP_ADDR_TYPE_DEFINES + * @param peerAddr - peer device address + * + * @return SUCCESS: started establish link process.
+ * bleIncorrectMode: invalid profile role.
+ * bleNotReady: a scan is in progress.
+ * bleAlreadyInRequestedMode: can't process now.
+ * bleNoResources: too many links.
+ */ +extern bStatus_t GAPRole_CentralEstablishLink( uint8_t highDutyCycle, uint8_t whiteList, uint8_t addrTypePeer, uint8_t *peerAddr ); + +/*------------------------------------------------------------------- + * FUNCTIONS - RF_PHY Profile API + */ + +/** + * @brief RF_PHY Profile Task initialization function. + * + * @param None. + * + * @return 0 - success. + */ +extern bStatus_t RF_RoleInit( void ); + +/** + * @brief rf config. + * + * @param pConfig - rf config parameters + * + * @return 0 - success. + */ +extern bStatus_t RF_Config( rfConfig_t *pConfig ); + +/** + * @brief rx mode. + * + * @param txBuf - rx mode tx data + * @param txLen - rx mode tx length(0-251) + * @param pktRxType - rx mode rx package type + * broadcast type(0xFF):receive all matching types, + * others:receive match type or broadcast type + * @param pktTxType - rx mode tx package type(auto mode) + * broadcast type(0xFF):received by all matching types; + * others:only received by matching type + * + * @return 0 - success. + */ +extern bStatus_t RF_Rx( uint8_t *txBuf, uint8_t txLen, uint8_t pktRxType, uint8_t pktTxType ); + +/** + * @brief tx mode. + * + * @param txBuf - tx mode tx data + * @param txLen - tx mode tx length(0-251) + * @param pktTxType - tx mode tx package type + * broadcast type(0xFF):received by all matching types; + * others:only received by matching type + * @param pktRxType - tx mode rx package type(auto mode) + * broadcast type(0xFF):receive all matching types, + * others:receive match type or broadcast type + * + * @return 0 - success. + */ +extern bStatus_t RF_Tx( uint8_t *txBuf, uint8_t txLen, uint8_t pktTxType, uint8_t pktRxType ); + +/** + * @brief shut down,stop tx/rx mode. + * + * @param None. + * + * @return 0 - success. + */ +extern bStatus_t RF_Shut( void ); + +/** + * @brief rf mode set radio channel/frequency. + * + * @param channel. + * + * @return 0 - success. + */ +extern void RF_SetChannel( uint32_t channel ); + +/** + * @brief shut down rf frequency hopping + * + * @param None. + * + * @return None. + */ +extern void RF_FrequencyHoppingShut( void ); + +/** + * @brief + * + * @param resendCount - Maximum count of sending HOP_TX pdu,0 = unlimited. + * + * @return 0 - success. + */ +extern uint8_t RF_FrequencyHoppingTx( uint8_t resendCount ); + +/** + * @brief + * + * @param timeoutMS - Maximum time to wait for receiving HOP_TX pdu(Time = n * 1mSec),0 = unlimited. + * + * @return 0 - success.1-fail.2-LLEMode error(shall AUTO) + */ +extern uint8_t RF_FrequencyHoppingRx( uint32_t timeoutMS ); + +/** + * @brief Erase FH bonded device + * + * @param None. + * + * @return None. + */ +extern void RF_BondingErase( void ); + +/** + * @brief single channel mode. + * + * @param ch - rf channel,f=2402+ch*2 MHz, ch=0,...,39 + * + * @return 0 - success. + */ +extern bStatus_t LL_SingleChannel( uint8_t ch ); + +/** + * @brief used to stop any test which is in progress. + * + * @param(in) pPktNum - null + * + * @param(out) the number of received packets. + * + * @return 0 - success. + */ +extern bStatus_t LL_TestEnd( uint8_t *pPktNum ); + +/** + * @brief used to start a test where the DUT receives test reference packets at a fixed interval + * + * input parameters + * + * @param opcode = 0x201D + * pParm0 - RX_Channel + * + * opcode = 0x2033 + * pParm0 - RX_Channel + * pParm1 - PHY + * pParm2 - Modulation_Index + * + * @return 0 - success. + */ +extern bStatus_t API_LE_ReceiverTestCmd( uint8_t *pParm, uint16_t opcode ); + +/** + * @brief used to start a test where the DUT generates test reference packets at a fixed interval + * + * @param opcode = 0x201E + * pParm 0 - TX_Channel + * pParm 1 - Test_Data_Length + * pParm 2 - Packet_Payload + * + * opcode = 0x2034 + * pParm 0 - TX_Channel + * pParm 1 - Test_Data_Length + * pParm 2 - Packet_Payload + * pParm 3 - PHY + * + * @return 0 - success. + */ +extern bStatus_t API_LE_TransmitterTestCmd( uint8_t *pParm, uint16_t opcode ); + +/** + * @brief used to stop any test which is in progress + * + * @param None + * + * @return 0 - success. + */ +extern bStatus_t API_LE_TestEndCmd( void ); + +/** + * @brief used to set sensitivity level + * + * @param None + * + * @return None. + */ +extern void RFEND_SetSensitivity( void ); + +/** + * @brief used to set rf TxCtune value + * + * @param pParm(in) - Must provide length of parameter followed by 6 bytes parameter + * + * @return Command Status. + */ +extern bStatus_t RFEND_TXCtuneSet( uint8_t *pParm ); + +/** + * @brief used to get rf TxCtune value + * + * @param pParm(out) - length of parameter(6) followed by 6 bytes parameter + * + * @return Command Status. + */ +extern bStatus_t RFEND_TXCtuneGet( uint8_t *pParm ); + +/* + * END @ Profile API + */ +/******************************************************************************/ +#ifdef __cplusplus +} +#endif + +#endif diff --git a/CH5xx_ble_firmware_library/BLE/LIBCH58xBLE.a b/CH5xx_ble_firmware_library/BLE/LIBCH58xBLE.a new file mode 100644 index 0000000000000000000000000000000000000000..542bbc440fc6c16494788d68e3c72c1f1113b9e2 GIT binary patch literal 1112814 zcmeFa3wT|{bvC?ZS(0sxEz1TSQHKi4F|I2+mgE~WaU@H!Y_N@Ei)#r&K01;Gx^a%= zOPZv<;+TX4FkncWlu!bMFU?0G#3^3_1_A~`C`}U}ZPU`4OYS8J#z~u)?|s*rwfF3O zjv)E}v`_N?9eZ!RYt5Q9Gi&a%XYX^`J3F)KjY}uIqdM;D7AuPde?1Zg5a&LFI*P{zb9AG^v?%xdDUlwqC(fj>w==+ z#ch1Y>?nSMeD?4t}j`+g(BN@9TBs zKZ4@_s_U2aLGk~~Yi#|Gga0pIB^6%`ey^|e%R$L+a(!q*Q1aWj4lN5x-s*b(_0Y3Hsa?lj4@!THt8B`g;CJ@wyeKI9pX<$S@RdzL zS@Qb!vq70%FTsZY4cE9&WP{)3Yy83w2BThQT^5WV?Yd-XF#i9^_3)fveBzpL;*W#h zyX%(k2NUc%@@z2SH@r?LzApH0zaCi{oRGLCR_zG#uGv=x6GyuipBhX|Ud`VRCjM7n zSJej-{p;h|VB&A_I&txs;J12pTos)7KiBWA4JM7bDEOc2ch3fsr#>9~PG0YNGMJpa z-n%B4JlgeV9}gz~H(uo%)&zOi6{SJ>n_Zv#Mo|8{dkyyw<~sT_{e#)99i4rBgXzxU zOt3oB-#py6ez-F`>~QNqmqTm8>>chM90>fN-Pueg#^}im_hsTTy#u|&NturR&Mh4q z5bfn1NpWcCpxTc9-hs$-Lw`qS*JUHQ;f{@&&aO<>Da`eBWCpGn$&6Skz!)0IZH&CT zGGX>K;Z6c^3=Rwj{UEPc0hLjReEDo9o$1}=MUl(EE}=TjU}x@f(bK;Aob?^e%R1I( zvb}>{z3H{t!E`2;5)2*sN1?@7ZJFWqTXVyxe5??`Q1^fssd-t>X-C+M zm`{wDYs$LfH^WF^FFEPHOlQ^w5qpjdb)kNH>0%*72~*`FsX1W+{hY<}?R#W1eS@7{ zVh&>u4k;;0bInYO=T$%xO%qxq8;D870P8Y6y)cVoxq1{bVJ63-p(K=}VNa#is*W2l z4zZocu4Jq9Ly2ue?aNlRcC6~%AX41}&?sfYnELuUnBrWzv!knbQ*UlC>$3(o53u5P zKq@`h-#<9e!MePp0IzOQ0cJzJ%YUYSXnkk@5Sl<_kCp4*vjWQc%pzhWNud_qz@`pu zitO8Rprjr+_5}n=;wqbI)DbGCYWU<@4&7I3|>4GZYls%-!7A%ie+EjI}&(^a_7@??Bhef$qUP=CZBr zTZb}|!kFJW6jmN%S>ND>Jm%_=zTw^&_k0#*jI%bIq2cpATDN4akpwMKsK^lMCsjkZ%G{)@S=cyp%aq*7z z9f~d6+Lj$+QN|R8Og~*(3Ql9JNJLw#tejMg=j)1-if0+rZj zITa$Btj8gp6!I-S7?QG`eFa!(DFZ&6Q{ADBnS^k}75Q9$TV?<<=$SEy zFB|EmhE?_H&LMhBs&rsu(ONolL>L@;yP8_qxsYTXauhSSK9iQfIT?<_>?#TIrmimh z&o*^k#=tGiIWn9fFVu+TM$^df##Oxoms1EsYD;DlB`|T%W2_$Rl5{|7<+`(-_xcF+ z_6#%+jtp?(23;!$((64A)2G>FWRh^pudV`UjMh!V4C*FsExoxkn3?o&Gv*|H$N>FW zc1&|vY#i8^4GwfgLYz`S4L#O&_GWtrdJt7ietlo(rVI=?f&p`D(i0}=O=oD;mcfCj zdQctHuDOV*Tf1><0d8w2p);4eJhOF8 zCexMaQr0v(YLCk!18CGN#$>tEa!HGiWOBojBno$O$;TXqjgom6&YF?_rt~lt zp;U~jfQ7hbB^0HNS(UT}NTNL}tj{x5)Li`K=aA)OsEtjB(^~8YU}B7%u|sf$mtRZQ zr#lA*P{r8lx$H$)SB;xH2hy27OCC+C5vq37GR_OVYS6l6cr-6hITF^JLwxJE4y2K0 zn65XsG1C=qI-|(18q5tlK^7j9zAlrNmd-LllGx_L3hU*2Mf1w${zN2}^LW(8ZRy4C zfyL%>Zho3mhGU^=dz01ltxqIjURTd3nZCAJ*hnn3Me%auo6?XeA>Ffl!R5fUD=M z=zZvgo7-DWbQB-0>{@fAX0&t;cS;!4eUA+|iVx-yzKb zIIRALDv_=3-X5(4(j(EP48Z(`C233@ZevBqs%vEs)?H&-%<5rUyrOMuTkp*iNLiP` z5i=mey4#Rp60ZOCL?RBd!lf)m64B9;>GMg;SJTjtRXMeQ)kh~x&mOExbz0`eZ6#L? z4)lnhByTJCV}#U&su@BTzRUXtH#cJ=Oq(ir+_#c#1PQW!cyK79WNwEFgp~l3=f+-H zJPVWcx~Z?Pd1Gg`C6hztiX7;O4Gvs+z;JU=SPE!!%a0;0m3c&kpGVW;~>Fa*WJ z`!L%ai-k94t@maJGYb*;+I|c5K2+7tEM$9c`?tDwp zCl)3u&JvCnn=2+pmz?M;PxgA(W%_#A+ey~6;<}z+IAM*AsK^opN+1+VmRBqmmqBT2 z5X2q~He5JQf*FuiZmBEb27a(zqO^@*KT$2`xR@+qx&yA7IK~yFG#QH>LuY^m&R`a; zvs>yxW(%Ot*u)t;rguHMTa?od!KG4yq%mZ%*ISp#2|eUGKI!3vgJD#sJ`@XOWD1ww zy1cKmM|hn>L)dWTj6=vSnZvH>>{l|4n%dOUgQ*~v`657$M>1tdR<^lY)um-)F2-SH zhZt*Sr~v~x>@%cEr%LP`W;Bt#1JZ6qG{-D-MV){!hhqJLuvnE?7U(n`jY+&uvi7-} zX1i2jE!N_4bE*P6c~p+BQpGs88?ml6XWT?!U1V|?QgN9Dn`ZTtUeoc>i)J%kDx|Vl zM-N-GR(g#^J2#mDhS^HCMo--OE?)(C;Xa7(VQYNNp{6OnBC|oLUyzpaX8zkZ^htA} z;n9PvPfF9$WZ5+NO7 zN+g)N_V?y;ZpDjXOa1&b=84c^YcLG4tYbD~8x&@NEYZ(;j6zwaQh)Q(S=FV7q5X7> z5<%Ecjbc}-(MYVh#~umEJlbpO*00i*Nk4z;%~=5Y3);l1Nug?V%NCuLifM)ypAh-I zn{=42wES!qsMb>Ql-IhQrzXa*sw*8NBqnxP2qqUTWlyg?C1n2umbht!9mUGSv^S7L>yDNMlh6t*G2P@PaujVjr%s2lrH2=r`K8tZzA2 zqrhO0%Vqkplw6E|>~LSeq5+xk(OIi>UT=nT&bLIykkm>x12wH6+!xI%1oYJ~Kn zMI*s5iSoD+f_}Xbo>y&+T;7DsH_AA&O-p%L$ys?$Gas6)?i|kcZc)l%lw}wuQGrogy_B-*v!Otp z6>Tyg9yt%<5jPvhke&vK)z=tHl*gpJIv0}+YnO1n`YyCzgN0!Yz!Di_`mI7d5|veW zYyBE29I1-QTt$sRHAPReA!$`F)?$-Pv4Jt8>>G!5Nh`Z?3N)F7SG>`v`B`$2*_;kC z%~YOM3ivuhdOn`bJ??sxytR9t#6)k_+1Jz2wSgz2H*{PsEj+BR`PC`(a@BBE``H}& zQJ0cFVLjV2nC+;;5y!qv_i%1wZ})Hq4h(F-s5;cy#U)VC<4%5b+dFU^6(!@IeA`09J=|H=m*M=K!%!%U zM{=0i=q|L1<*U)ICa%n+xD40jASV4?%QM&}&1Q0iWw~X~X&#y+EX7B&@fh8*GEBaV zm)xn!m;MbzVG(x2t1<&U!y9#EOh(J6JXisT4XJSQt?Fu>4Ud>`wrSRA9l*$&#_Bc9 zLt$0=u-khwTBrcluDCF6`9eaiTZYjxGF@+_EB}FN-hlu=BbXW4=>&TKTPXGdVpOZ= z27G;d0M3Q~;7Fp(y>w$C$=V@@tlVa-*;SvPOO?TzjqeyF1l;OkeWp*(d^Gj-VC!^v zBbF3?GNN+fv+|YiDU_#3W)%PSR#zXjM~%lYibdM9oddaU*5tg@NAq}0t_oC_Xy%ny zd85XM(VZKA?EAd*KwCdLWi6I&2A29SeuHeu3? zwYKLOrBGTno}lG67Kjs*=?w_qZC*HR#iQDO^x=YBeRNSJZ?fCSe_n$5U+ zKF%dFz~PgQ$f{hNk+XVP0cJr9S_o7~$tA{*%Z-P=9XldUB?P>#C1P5mWVK!0xy5xo z&R=(2z!5*Q|egO($vzj&JVK=ThvCQZ!TnH zuqCYSM0vvbsY=GF8l$-|kHom-s22x3?yux=S(0(4^g4RhCGG(ot#Sx0&Zc|;-DKy9 zV~+&uRQ5HV4v#5k!8|YK{QQkLuD=Sey7+~QT**VI(9;WqiBci9E}jedFgG%J?wU+b z(j%H3NO2ShnlDj64a}#9Y}&EQ$~6|IXf(fcCuaA8KB&~3q@wbvHxD`FRRIV^zE0eY zkzOWgK+FU@4#Kr$x;ybUM;qf2?Q z=9x)rt0#f{d)AQ3ANNh9x4@7*TGk4P<|n#uM&AOX#plc9=`hczSJ1hQm-zxo zQEAS?-a<}#n75V-r(_9HmO?nobtezQv5OrnC+>-o7&Sm%-l5%{SnXRjcCL zAw5_+t&#(v^pedCB(FcCQyTKve%18nY#wlp+4#0_$ZPE;APW2XtQKdi#IxAQ?8E!K zm~!C|FP5k%)sA62@bH;Kx^EL_ie!3YCteK}d8jT>SK?u8@!7?97V$nJmiw`Yh6nMg zqAe{ua=Opzr=+{L4`m(?grXS0`>=h3SRHc1C(NTvJGko;i-TH6awZ@1>KxhP5|%Ik zJ(#eLY$1=x2A&}qBxy&p6Dl$6o#X4!ZbZ{!+JueVW}MhTg2POpn);;cy`8)kTKgb| zw1KV-TjLq+%k*@nw`!6jb@E2J)FxNt>o<-4@V~E+p-SC*tKxf|usW7gTIy!NH zMBetrs*6fo^NXwC!8I+_utXrrysli&>@M3%Z{6Ky7EE~#@3zJwNcc&$?80V1m;z7W zO+BgEVj0#oV=Us4AwI+Ba;K2$UTjr%bm@ZM4>qE&HfOMgk@o{SFe@+MUm)br#;qML zbOB$9ZwS56Ioi@qK6Z6p?OVL! z^!V$1e)|^mdg#xv`;^7wv5XgFUDC@-bPufGFWc5vGpED7|59XPITqiIz z*vsd0QB9&B+C5*4H!qifG#_|`bs_ZC6pZ3&H3yv^o`K{CHvcT1{hIl0*@;D?9KQDw zm(l(UMbbNfk0)r;;J$LR*J#-8&C4xv&mCw=h5gf8e#AzHi@=9C71CES-48g`9grWdF(9^G5PK&a=i=>Q4(*H-i zXb8<_uINCkkA=Vhih6EiL8vG;uEqjH18O8JV_ZmPx1bZ=f&}1Eq?b0wc0;%??Z%zX zu1&PbN|%|a?s-hB8z!WaG{)nar>mU9TjYxYGQc?>SKS!JYUxX0K6&=bmXeLw-b5!9 zMKm18K)WV}25_!)-Ybzyu-k2u(9sxQv%}bf!ZMHEA&L15$a4oS9~j)sN0}hjc8=t* z5Hrz`RNwP#6HTJg2lXzFkieu|6rsa+@vS9v>Q`K7OrVhGQolx8`|92S?v#5|3%s)9 zLdmxyn$md95OafqJk^Pz*=YxIDFjLyMz)Y(M2;qwF3yjQ;`ud0qBvYyF+!sk54v$a0HbNkx%{9C35=gguuAFln-6x? zM0^AWz7jU|m${XMr4BS9><#FK0$#_%bcX}oy0uODK3O~w+T4*H>Kx4rv!#45H$Wg3 z3oR+~qQnwKQ|a+^rJcu?6*gwWc^-TctS*S|LhROHIgTPW)`YWcc2k@*t2a6?hZ205uB%MAYOscl zGL~gj)Q1bXnR|FFa~7I#@P#29DB#$W5bf;h3YRSCV>&QSWM#=426dAYU0j*I*ccRw z>S{+K#Nd#YRZ*{w8pib2j0qo1xaDb(8)w#gCbMzT=b0Ii=rDc^eEye@F#gzcrBK8h z%TS=cLblar*{?_nXVFr%0GdX&MCAmcP$Ce6H~yKyA}+PLs5M`Xhz*ZSm<^4@;jk!O zn>V6C|HjR-M9JctRGc)+oYS>U$%$|a45KRUjiRDE8X0BpmbGZr;Na!>LKV^(vRum! z>8MqQEZsBH79BFBJv6nh=liwzgI+c085+(4V!0Va9xZr{3Gu<72Z9K}5$I&F>|o%C zVhDaGN%n%Ve=K^JEu-3HI99gqJ@|mq3e1c6!J?4CLs`Y#>~pK95=#`ByGGrosHsa#w!NsZ_s^nm2ALx1D6GYj0e|eFwr|mI7p(1c zLnR9f(;PqMNP3-0clM^AEpyCTdp%P;xai-1or5XH>W#>@jTu0KyrN#YFXRlvLnVM2XhT{+F`e>pPg_y zz%%7heNl9wj+JY3)#yma`t~jlvilq68{~+dc!q?nB)Tps~reGRy z)KP4`Zh7mP79O-hR%un>QXTYE0j3!1vp&g*=`+eRWS5XIAK??- zP#bmx*LKRg-)<^niBpZEdNcDfp(YZBBGe|T*^(L-e8*h6H} z=sXG)9@tK!JuY>^9``(X-tF<Z(f~HQ9yjuMo=7+RxUCXr za$bQ&NWKT8b-s}=pNtr~qv?q!ZZi~p^6b*?F~cX%o>^p)z@4CkDf6E^yFeIH!Z2Rk zIu|MAgT~`Md5$bP`jclbd2Xn{$QX-jv?tFlq?^I`LEN)wi{5Q4aB|~GB_2jQVdeTy zk-Y?lErSbJ9FKEdic}9d5R`4l==`wInT>vgPk^}&3Y<)lObyCB)J zpQz{Ze2a8;tsm*>K|V8h{w@!?BSGn_kqAliTZ=IZ(x#)ceNjxdIM)Mx5g(No!zbPn zOg{JbvbQOtB0TV~&-Gwjwh&98G>w8jYF4-IF3_mxA1-h=aYNco(5%Q@uOj)<7zztX z?=tEs1eX~I<>^R_4K6;MWa{l7>5s}Hksf#WAkrH@r4aG+1ktP6YLLl6E7Z?hxx+hA zNP8si1&}%zK5Kqc(fsbz^htl4IHmBWs`5Hv7Me4Vm$*WIs6oHETynrGARX?kCKP);MKToThv*HwlG3p+>NVanfE4XpN@;HqKdq~AC&p?D z+OVQK!;yjLw{AHPR`8G@=Ku1Fx|etzBHm1bHI1K%OK-Ik8!V(mMTi}Sj2c)nlc=J2 zB*$aEd9@*h!$_cujj|VWBce{F0dcWQ|Ihl*EctL9(Ha!&qlF1zUv2Bki(gn zDU)I}M)H)(XpJCGFfY&07K=@{mSLnql5DW}t`4RdT{djUs#kgiZ5?aZEn9y^5aGIo z+{JMgm-z;1i_%R&gr##2LZ(UgR3z`hLW(fGaLab?3-NVJ~j08uP`vg)cx_Ub*!J z?5a`to^_IQo;>CB2l;{#pafzgB~^omkwR*k#R}S6dqhI4Cyw$~8=vL_O%MJ&iz5+5{+cMwe40H6r+Nsl@b?MFKlqU05 zvq_c9pGJ~jX#Pk(N}#}~oR|3mGG68j_(r)y48C#lRQsM$DB~9DatcJJVw|-xQ*SB| zbyEezViQ2B@)}Z7TxVYt`t_!Klzfx9o+^@W=EPpfiY9p`VI#Ia>X7&gP3tH=%6yJq zEcs|6M~>-rGlBCOOsvpLV&1fkVsADpgLnRnOjE!<(v7B^rh)m^Q#4U$J*K5uY!Ob+ zRC(TvE2?`gGTMM!ynT4WD(~Nn5-^&8sX#2&0@E~=VhbraMN_**N0xIoI3ZcNb~&0B zf42!|ChV=AX8zuc9CCL1TJ)2dU*uD8#ua(HH+=FnIuAW{5y{717K_;09p%606-m$l z@wdq$zbN(jFOx-5`X0M}>l*Ko`8(RJTZZsJtSgdTC!a9M&lP4^9Ms*a(!I|a1%tQO z?Q3Die51fRFDMv8Wb#7q$Hw&KzaAU&jwUyn;;_=XHWUf}`jW^a%fh{&mZ&ZM3^dw4 zD`cnGqO719to24VD=0R~d$Cd0kS2`ZaeEgi8XG0@3Q(WGx%UN<9XHl594T6HRw4XK zw!A&6uNM>$*QkFTrFdb%-h6d0D`1P??X$(gdr3ww6plOgzH1cN6}wxu#r@qPY}D6b zRZCPD1>S~DiX>i!b>4;Fg;mc2_M&;EkIa7+)`fAa=Jt(QyhJ*PUlY>zchz^4?o~wa zsb)SK?O2w@hmF&6=+HgdRTTyN(Vu1c0|xp%xV+#nzkIDS&%@0|_+f8GnUl{RxA9Yn zD$37_vDx} zoAWt1j)vl(aj*P-0BL;L-21dAf*MnwmU|35zzw@5CeQ?#9ad+br_*@-VF6E6PP?Ff z+h?nE z21_nd?h7;HC{&--n#Naf_}vEpz60Z_7QRQz5tTLP@ofcHG%N}>BxgKsGrEPT7mPC) zdkf0~^5Y@;3Y5>0EebEBtG3|qBxAmZHYcklTKq=I`&FjA1A{aU-?&C`&IbKZVZl~m zo`sr4+%K={oA+pS-~6NME!{*^BjWi*P73?mmwPNfc?#m)Oytd8R*FIk$yJU-B!w~_ zsbCGltSYo+qy^)SD&t3om4&0qO#Djs)+$GCgggSP=R#AP43q`!tfZuakRyRK;ig{nKjmXn27uN7)hu50U5y?x}@@&7_-2! zpxyldj^#^d6-h385N@=1q4ku@+CiMh+#2$8@}iqb(ZKez9r6mebU>OizsXsCWd7&g zcsHK6C^c3%&dXBOxLT)UEUiSbp&<+#OfjwDQYqwG ztFzur4CD>XF@98?V{BKatma}T#BHL4MS}vj8KeF{jzp6~H|PDZknm)xvxVmg-K(m} zy6$Rj@Q!y>|3~Avf4P3@kzG7A#F5NMMZ(RqTpviLgUZj z_2FMXj_qbP&wTZSBX58-Ith`gfzD;S654nAgRv z>UkUR6I1ij_y`531?wRW?`cJR>w37NB3=)t1yh479=&T%QS0{}y}In-vWIs5I^7Q+k9aB8JB-nT5p5SdCyX(fz%1W5D_`K4f=DL@Inp;YX zYW9@|(;lj+IEwhMd}BX+Yp#LM)cb<{B|)(BTP4B0dyY0TmYVWo!D%HswvB80JF-YwDY;;lBd(kmfm<;Y0Y(yNt$Y|c_sMbo|~GF zOsG7@{O!B4G`N1*k>clG-+5k7@HfYIGM@9=f>iUTOYga{=DO#Cn;zMTct2gnv@zE^ zwmop=!+S~|v>cMp(mK$bYA$L`Po>08-d3{jyv0F!_@w|vQr%GYY#{lqehKm~&G^@oYbu^a zxs(ReAG!Hs|5{m7aSRl!jvD>Swjj8tQ8Zt3N5EQPu~&vEoyL^zdmSk)ey%5&@=Eb@ zZ9zKsQgG((1I;fTWK6~9mcZ9SnxzgjU$bn^o_kB~YfMjj9nybvC+pII<|Dn&9Y3=9 z*ztM0kG%M;<41ma^!Sc#zm1+YYM*Sfzm>k0ikvGBdqHIsOHXFLut6{(B7llxJq+nd zA~8Lb8Y}dy1ReEr>VICs|H=e?TY|njL4P?xvrki}`d=^-7Gn-hwocB`)d_lbf?k%O z*CgoS1idRk-{aAf5&u(OdZq$DJ23ltbz_4+0{EPf$lqLG z8)H~w>OjBd`7Z)~-J|~m_{&~;8$tW_Xa)U2&;KmYui{dt_BDzl6z$FNP+e(o4nR~t zN`rRLvz;&HF9bf>qvJJhoal>qO4DZQl#cW%-D2KI3BzVR`V!zqkB)ScIU$k$2|*g% zDE$*;Jmy%gZbHxx5T#$kn=eP?xPEOfk$omf+Gd0SCIy>;&-Cc`1E1p2Ap|JKOklfX zd;@U`u5{q!- zwHVhzG6?F4>g#x;VEqE#=Hu4DTRpYlQ=1^`al?BEKteL?0%}_z9u4|a=9&oop(~u7 z307zNn}_>kS?(|n-SQnHM`2No&lY1n5%kCl{d@ySeiPj(>gFr(`uX$3cjkSmo(x`9 zABY9)<-Slt1}|K1>DbUWh>sy8#i2odvZ@2$O^iJGz5TAsM(}1S-bBH7dSPm*)0M8F zuzoj?30I9PbpnxM~!IXP5w4ulP5O$JAtcMk>Q9<{%&BC zCpP&n0H-*qh9frlF9VxAvB@6-uByNd?hr1M|0b~6pV;`%1E;3q21jiC7l6(F#KwOS zI5iD7IAY^-%&R<0*fRbZ{}k|3Cn=xU_#6{Wp4j*^fva2xM{InK5zH?gvGHqxQDJbWo|t>bSa--Z3Chi{~e;~xT^J4f~KZgrm6Y&8}*^~dDsSY-Thpsmgm z8=u`xiUSTDvGG|=%Q*pptHxD3k4XEax$jWSyY)-N7FN>00)Zno{sdsklSfQGF}tJ` zCs%O9CeQM`9%xzRW8-L216theXvsxIYi4Ffodtq6e@}0M_w`U_CPzCdZ7Y5kKHr66)9uW{lX}Bm?Oajg| z4<&G?A04|zmhoH3vAHMXeb|)1&*Ej-vhFqFqWc0i>xRMLw1IqHtS^nYe0{HjU|4ge zkJa4QHya(!F7T+&>N)js-0=0yfPxQ#XU_C-&E@O+9Q3syAobA>mcI7^`}%Hy0@p>t zC(7>#u&?h`%)hPykNVhWn!djT_Vqmjee^eH>H8EIzP@ec8ZY%RpQi6?z`nkJfCAUt z;S;6rF<@Wc-OzU@c+|(f+w{ExOnsKelTji3IzvCZEUjP#t5|VLxSk1uW?a;VDDw7b zSHZX$Dsavq(?J#HE&$_IBkY^ zjjC66KBhX;dtdfsPHO$hgEb%f%yHY9JJ5VjBhx{;=9YuOr!!4Ol`v)xEP7Jwj%`KX zN&ekD|xtNS;<4&o3=+OzT(@zI=`m6B&ey_9n=&b z#V+sP9b|gB$2q;ZY08YR&U*N)L#2-uwG^$qf7kXOd^%(4Kk4zRJBts$@<#e?cZ@mh zP{hBfap&o~-bh!yC_C6S)yD$FP&@W`&E(gP&)ChBUDq;ZB~v%$g{6CU(#8ju?t1CL zn%Z9+|Mr9TJ;69jADgp_dE9xp4Aw}GycXmF6#?_6*6AfJ#D_w^61ZI6aBdb3(wd?EBT7An7qGfPI+0 zD7_~JD?!f*1x^gs0AJ+M>w%YgH1oF6qu&e6d8fJ)gAD+jZ>l>H?bpx$iDC=RtJIws zTmi7wNSD57ESW6nT^IUK7Tv=h{b#_DeI^InK+pI5cLOi;=&OL4M|G2fj{(d!619Q$ zi5z?M$Q7e@T<=K6$$hlXIGSnZzS+Hwt`)j0IP7TtVYxl;=pp>AKz;e4qxpyauj3l) zF7Eq>A*5&=si7r+E#M;TJjj{o;RO(3bE&&(T}Ne{#^}XiY!zYl9Ov?qn+s ze`W8E#C-1L$#$G<*44>Ap7?U>tzK^A$u6Gm&T&6)0eA5l;#+jOZ6`Z++~`|Kw2*Nu z6p|qpGVH=Sw{Mo~>BtP=O+S3EQ>VgU42|GxK_S(}ulwRBU$D{VPnKoVE)-%A?5z#r zadWiC*AHD+tiYz42FziSoRRZ~VX%Ul6HF+*=(R2*s%Og<|`FYrgl8L&u z-&)soos4p+i^oV{( z5F399aD^j?jn96gaxrdj#KvC*oN}EivGFeeHhE&>UkqF|6*o9y<97j9FhV$D!PVm`XJ-w^_C7ZyQ2MqCcA9W=aK&`G{U?U4b)8ZR@LVoToU`doqc(w=|2`?LW~DbE|QKn}@6X zG=X9h+jC;RY@cZ(aP3T$VLvq2?ePo~JO2BK83`QgjMXcvKde_Qi`413!4X@)i zHa^S6>J_o^*}t2eiH(0AaEh6OBR2j;z!i1m#`lu@KwJGGHhId~UKguTIV%Dj?ODzN z7;XWs+FF4!-r4@sDzN6#sLokBNt%AvfvR_?0I~6Z^L4+s(D%B#d2zOMddbx|doZv6 zE9C9rW4thB*bEfa(VEFpxLkYV>>f=n^?DAz`U*A#??+4GE z={pF9udfgK#<}!yxHNsw1N-{!2eHMJ;jiiYdl#nB`3<8mOdp3+)Ati#Utc{G6e_nL zx-gCSOZR?c(#iufsZd`Dlu;l3=-vr^3lz$oes)<}=`jT2Rjh$I+_Af3`pR*!Z2WzO zOEK7IslYjdOoK1?IizordBV>iCpqC}1P+6R`w^p@8LMBk@QivnH1^hK#xA%-|5Mvb2cLLo>lHJ;S^Dg(Z=Cha znx~6en%eGtZg=DE4}a#7ijumv88}fxUBXEb}!*1EQau`iCGjiF_P$b6JwB+JF+R9w?Tq%%Ts`K(>)L<1 za^CLN&K=uI#vYme;?K9YeCOaiqztE<_LmfK)jkV*1xFdhw-~ZRk@7?p<7(0Vh{Mk}T^^20~r8n{E|H#O}lCmRzc=$iIw|px+r*&Yx?juy9I!qlqHrV^M9s8#@F1h2V}w(Ugrr>C-cwn)RgR=~w5c z#~z(j-)8BhR_2O#Y2by>6uWy^aBAq)H+`p$1p}0eI z^T(FnuyapWQTpk!;9wa-Ty_+H8_#dLW#^UMMd@#smcH$3(>Z5`FD)$da$rTJmk0CN zxu>V-$m3JiNW_-qNFir0+4D?m&s8Z+aPNJaW?O$9Kz)-uAX1J-Dyz^*0{4Va|=|sgGj6 z=vOE0JbY5X)EzzktAjk}x&3d)KfUzE-DSmxkG+wuJQy7L#Np#5*q7tZ+>UKquQ2P( z_~y1|YrE^7I`7Gg9>3(V4PWg&wD^f9ingC!iOzKDgb9J!ed_pMEZz^^zN;n#2ge_} zbMXm{=kGj!X0ZE}i(Arvae|ztT$MD)t&j zHXr?Y!=7I>&e`92FunhTKx2L-_?v?@xBnpc^ug!Xj{n>Azmx_IyYFb+dB=pJ^roXu z=8oXj4>z`4aq!5`?l}JR{zv76CDVN2gKXtti)hpy=-R_O_FJwlZoKvpw*2%>Wz)%H z+uZ%(tEJx8Y{Sl#tFcHk=qwunW%aB&JV6> zWbd^TH9q~d=O%D>RCj1?XL)zozGKHGQ>JFy>%oyDhkyFE2d^w!x_{@nWjG_Nr+;g% z(Z8BP?ztAXmIPBP-yYmA_LEdR7ukCs+QtX2-d9-}+|&q7=a&WVy}G9Sj?iw`9^S2I zaD!uE8Ho<$|WeXqNw;BCP>%BJ|4IVCOn)1;0DcJ9x z@l~_Zl$WCYaca~Q2d!FXNU_&S|3lfBgKe+h-cWK|j$h(1a@V~JkB*Tt z#yRTk%Q;$@-jRbxw%qnhmdn!XzkTonPb^;X`t4K4je8tB*WHg#@>~5ykMm^o(hvJ> zpDn+r<*!#f)OPO)(~76Q|Fy{t#aoJsYl?&Pl;R1Wzv`jPUAwT8|7ug~nOA*rZ~b12 z^~CRmdHBtjMk!m-oNXSnp@8=s=2ML=*Y*$|GZ|~-l7X1T(qaX?db6phyU&% zW&!@Oz3l!HDM3T^k^N^8n*K}jC_0ckD0Rz2fDkqFTeNT z{xMTa%Ifc3_)_q|4W`FJu`bTIA^n7#4`g&cu=IwSnV$`|x9JGBY~?*Gm+t0xt@gLD z2bxPu?%H4cEk4yCPi)wAR4i3A#iV9ExvQCTnzJ5X^VoNu`uyJgXuEp$|52o|elJpZ z-=kxu?nVk1<4pmiIZ7p42=ewgtT%4Q^-;*T7h@dO+}-!~VhbCEv$%_qySuJmx;MSj ze=@A;yW3d1$39v!WnocGusit6;3(>NNzi)m{yopPjlA*r;a@zs^6Q*c>zq0o zmrI}h;@*3%ns?Qlz5B)n6PsEpu}+wJ0@}=~eJ2FPY(2N5r~8{%_Tg#Oy(fJceb0GU zKD2%6xh-M;eBu+7Dr$Y;`kGs27S-I^Qk33>8TPilqmGy*W2X+kHu6jbb+Ze$5md?#>>F*s3t}3zG_`nSxdT7TsaKEpk9s7m}B};D@*QE2q zrYTn}-`-k@R>*f;(2I&@H_9UCA??;zQ%etxiR8^r;a}J8r+(}7;OY-Az2WLMj#;}~ z&nTHXa&)rj+IB~9vv3jLObb`ZSgY>{S|7c+&E_->rLQBVsimc%6vJ!ZhWktn6OGps zu$nWT=?(w7kk>a;2W!yO{ig?u_A@TT5R^YMbx&n5b@Q>w6UyS;#lc>gk6cy8I$pEw zrQnPEuX#kr7t_U9W%mBFSHck^)x7V@%7FT7f@d)=V{GFd;mVp%j$e9kdQTS?p1ZRv3@RZMT${@LgZDnh=kkp>9B};2oy@Ho}%J(y+*FwSVgWFPc3~{ zEW3jdr`UT7gWO`-S@r$7YahAd#1q?M?`bflii7G%Sm+rEnpTKhaWE?)fQr4R!}KWj zo|Y@^@6^)Ydsrayw(Cb}`qwLP_u|-2ApQA-D00O(UGE-uIp&hUJ@%23D}ui}D(n2m z>a(y=#aPi;?a^lfbAMXhSb5$N?QiP4N0C)Z<$2l;6UURqHsEM~wKO;z^cv6q9N=z` zZU>G^y;Ramp1N`J?hw;tG`0*qtB;d+cOol~lk*2rddA7SJIgFMPCsOUKj_hM`%IAd zqgttSbttd&oCLkjqc;Ql{@lBcN^L^09duMnRbNC;l=!1ssB~0Il#cSF^aWx3O7BY0 z*CgoACFpM?=wBx2XkS?MPq)os)D+rpwntwH+7JI>&@X%Xn3nf>^bNp%`acfZPd}e8 z{M?hj4fs`${w(nA9{qV>KY#ave%kZ@5^z*MCI(*tJ?!~2Kiv0MH%an0FG1Tm3O?DS zeDp4%(w8LUZ%oinC+HUvborQg{0kEFSqb`r1l^aQw( zUrf+HOwj1KV)7Fc^eG8?W`drVpqD1-wF&yt1l^aQHznvD3Hsv+`hf)fe1d)iV?Jh&nF)G+f?krK8$J4AXo{XgPLeZ4Hiol)J_Y}%|Ctp09q5@ZcKSaHyuzdZ z5qPOb|10oC9{mdNDv$mV@JBuRXTXO%`WL`o_2@T%Z}VtA|6&@{O$tr`{fv=F-xRhD zUg9PNmB3>?`fb2tJh}$>$0m;Oe+2rwo@&XBrE!Jhy><G}V_x_U;B_9&v%-FRz8v&NJ^w3!uk+~5z^8cnKLDD~3@!aT zK`-;@y`X1%^v6ICd-M&U-{;Yv0KLkqU!Ml;xBojpf76ryE6{#_a4+aDdG-BoKtJWt z4}$jF@1vkE@Z$R#XutpXJJ5dr@f>KsJ$whW-=7=-eXAEA`{!spE|>m#iL(};n9~1J zkNzIQ`||$|dW+|O6m+#m|0n1f9$f_6MD0oSNAsHsiSM#dUTKbb>XeSgvx=Y$`d&A0 z%#=<99gT+-!Bo&c^!%$p`}+P6^mU&9+d*&g=syPiqDR+({x^?a44PxAx{BakpwBcC z>1hTX*{32{3Hp@Ke_C)3=xH8Jdq?hMu}68xP`cWq&qesz9vu&#lJFBl|5Wg&kdMkw z{X=i0jNv2sQ-V&&&kF_Ae^N+K7kSP()hQj7pVHTxH`W_H(D$TA_k(`QqjR9|_vo#l zzu?jF{GBTChu)}p^pEP3(oueu?g|4a{m}&dT7r)H*XrQUk)EhLs)OC2qxw-Dd>HiI zUifQ4f7PS+gO28V)xjr0`|Z*g!f<9y<^7p?%&+z>310BuBG(WRK`5D3g zfqzurX9N#{zRL4I3_9!4UkAOwqiN4b-^}3e;eTo9KQs6?=ys2$d}QxgQXY#!f2A8e z`d<(}8lPtcFM*Ewhgrc7K(F!Ssef8X&klY9|BF2OSWJGl#2>Y{IYLM6Md`I+_&LF^ z5Z;$BMqjzn^B)g7D*rjbWYAH4n-iP_I_f`F|JX2mt;8RNS9)CNKQH(Lgpcw!PvlEO z|M|gb@ITq3X^+UiKA~UfR4A`>b%Kt{NBtvvE8UlnzcN8b{in)*F5!QFf_^wbKb@do zPSCF==;I06zOTTOtWy8N_E8_44tquG5%mv)qoz|nvcJ-i{gs{(1^`Wc9^C-C-lM56 zY7dJ<|7D@S((g;qI}-FY3Hqi4eOrQ#?5+C0l<+^4prig)<-eKme<4ACH$lIipri6o zeG@|aD_xPGs}ppzK34gtyp&#%kU!6(&w%}-^IMC8cZ24;@9Gu>t)Mr0^x2@J`n@Q4 z4`|=M=Y#g``Cib6OdU!-4cfO?FKFLBgP?tTjDYs@|7W26{B8%$Gkoe61$#iR_2{cX zN9*ON=25;lI3w5xfA%lD_|C%t&~$90e5c}X68?`l|Bs0OxZoch%`ohjewd&|G5D9@ zs=!IU35bYsy%QJXn?%AnhM(?e=84}nnV0Zi37V{0TnxWH;eSbjzC1y1PS86XT@8H| zSkHVEG%@=;(tiPZiD0Gw7wEGa>|3%P`15bDK z&p^Kj%szttMR@vPj^R&8(9;~f3w{-__^zDR|JyLxq(0GmcjMrv(4%=w~Fp>A^1@t@b)KIH8mw zQGTMYI`{)ee+s+`SYd&qyM&$@obBj~gq{^#1bPfGFQ&iS`Clsjvx5&hdRXW=!F7&S zd(;M>arDXJKQFi+H0{CipuWeQ|0&`>KX@K=J0hEgi{h$F(`HE3po$%`!s7~IJs%xOWI{BVaU4vv)K5$gG zSR|K-?-Hh{PU5ROLou^aw?yJyB9doFypo(cNl*QJiDe0sUw4M+IYacHA*Pzounm&t z2K>%440{IkG|U%+OV|b{Bm6q*Z;-dW8YCADbxcWve#%L{;nX1cZP5HSNWAiLSKR`M zy+P8vK++)ZcGWGCyvWy@>J~_97f6m4sifpf-u!d{L7D+5pCUuJ$ zwq9!ke)9}iKOZG2tgn+?>ziq!pwG;@f!6z@|^S){QfdwYSdtt9<8GbN*Ex&;s|Cx0E zH|Liv3x9rIQrXxLom&5cTwNnWVW7e6*63?uq1JvU zkj-quhr+^0dUG2_x~a9JW2CEhQ&(>TkOLByALB^ly&L|>i@Ye0# z5P0RXfF{~1G*LDX8%f;`e;z!V@+^O-av;5R#o*8oewBouD(}JPv<%os z%0;4Gnf!p9yj-O<5U+kkRBm9CQhsWski%Rp!|$!E>fInJIZ#)ggMr$)2KSfqgH(+Y z#KxZhZ0`%gtRRqGbk+kodf{x0Bhr|2s37vSbxWwbC1?ZuFa37E$w!B|S zJ|BX@5gUITST@!W8-F5j>O2vb-#Re99IRZ2aYv=f}0+h>g!`ZRsaA{s!Qxi&UQ2_a?Kpz_4V=NTIIvD_{qCZBj0a4N0x#3ug{PoCK1uK}*=Qh8#N zzuw7ndxV&L;+ud|8I>nC`CEZ2c4&TyjnA_?OeezQ{MUljoTxD;Hw&Sw^!dKcMo&CeJfZrk~jK*8^7#sywmDF9NREt@bB2KF?ZM zKS^x-Wx%N+)kAE2o~5#OM@&BZfp*}kD^#A?q z{65N~KF|>xe~9uql_xen&v4loO-w$===V{6SmlXLo@czw{>0?d{#R0dMCFN1{^OK) z^^=%g#)WtHz!J;cWUC(3ic zha)!r3zWZ7<%x~|B5-P}$`c!(XVWY_#K!+I<=?0B#Kz~@HA@e%@wrKy;zuIrfQ`?y zZKj{t_~R)5eibJ+KF_|HJhAa-Q~m=gPi%aijWc;-<1YZN;v@);*!VmvWb(wueE4U79}R1xP|iD0N{vCo^xgewd=GH##}$8>{7)!8n)W?mfys8GkBDBW&$)4p=tc5gUIVaOz%_CpP{PU>m21 zjsH&I6dME_vGFejW`8mpe~HN_?!uM24>veslOF)C*ocY-M{N94fos1=F8UK(i^{3&Ongxi5@uEQ0;wl_s=`e~ok|G^E8*!Y(M+q#U{_?H2j9f*z3 zG^f6d8=RkhHZz<56PrAnQ0gnV!4VsuMQHNG#=i@=>H*x~h>cIW_CenDyk7Ysf%0s- z{2|2~@u&7-#lyspD831Q=6+T2ZR9_y_<8&(cl;NCP3C3b+Q)E%`#!F@hXu+x;_}B8 zvmcxLgyM1dV|9<%(#&~M?UO1q9e?J2O)>A416l`&O@1tJ>M3&cnXB=s$HpvT<4*%l zeH}MAV&hi>Q=U9x@`-2Ss(KnXIAW7e0oVR5x#}3!Qb!qLN!v5(Pi$c)0^2;F*!WCm z>hExa^V3ZGlz&6mavL?SzgL-GJFai4d|q7NQvM|Tv35ah>0}#8J*zT)duaj7%9PmT z&m%vl)*&|jCBW9t5F393aOyeS;E0WX88Guc8-Iz(CuW;Uv0}mzoBT(Bt=}Lv{`J7t zPKk}rI-mMBZg9lLr#;L6fm}U@So@EP*Wyp@KPkQyf6Bk3_%08VV`WY3>rDMKIq`Z+ zderj$7y8EKX@9e!Ulu2WpLzi|IAV);4zSsfn0(rB9fdmKBR2UfflUvw@jnV|WkGEG>wr^7aDyW@{uh9)ju9LGY4Y7(8L{zS1GYYs z*!VvOPQ8d59I^4)a7~`r_+x>qUJ`M6cf$DNz$$0w2R92>?JEN1yimErp8~7=yUM@a z!yFQ7zo-0%@n`P$6+cV<4-~U{na<bsVq#sp1Rp$M9vq-g+vOh2*d zXFqRcN^E?l+3J|3e>QlM{-Xd6oBRsUR>z2qKNGn2b==_CpUgcb5GQ@2c!^DZ2KlbP zA~rtbWqRm*yUhcgVkLqjHu*)sw(cV~{+Yn3U*HBuZ2U%GlP5NQ3vktc;08x*{1kBQ zf0FCI&-zQnXW>unuM|(iAFBhze%KT%6dd&!e+h8KR?Q=^@y7y}zkwSZ%c53BI6YTv zWmgKm$)5sj{SvY9PX$gDseWSPvs|qoAU1w2<;SQzvGG|~t!@yL&v5OyQpGAyZ1R@? zTiqZwKFht9jT>$Ou5!Li1xGuVmn!ZCo?E7PBl+B1hs)xc>oDzO^AlpzKMuHRyvh?B zpY@;Vr|qpR5VMS`+~HValV@1loAvcf1V6Ysx@EbD(~luc{aNdTN>C8R83KNzfY*9ynALrZ1OXKt148U z*!Z)7D>{`=Y`83=&jDM!#KylHIF(X8#Kym$@@~(K*!T|sS5>JzvGE_Kyt5B6`Ap|y zxKgL6Jh90?4Q%r+V&i`Uxatp7p4j-$0$1=wKR9CJ|1)p}H%s7%jsG&RrJvaN-v>_d zfhQcX@m~dI`sqVVKJja~QhWi^VUs@&y3!r;CN@61!qllMPi%ZvsmgWACpJD!pZY`P z6C0oN1WOMw`IMT6TU9lYVw3+U=-ShiPd^JwY+=U&r)H=Ov4xes%axt=T`aqal&?{F zVv}e8V75*66{(pj@AnlmDDP}bZ0VT=T=jO9CpP`%z~!7Y!Lg0cJza4va79mqO}-s` z8=HuYe*ti6w#pM5|6*WkBgDqP4A}G#8-D;e^+&3Q*!Wpsd(KO2{PzJ<4{b(FKJf=} zrC68_oBR&Y7B8{!uLMs0vC0!0{~9Mxoy4a9df=*Bl_xg&n}Df*68;jCPkaDZYOcx? zn>_mslea#E@^|8@Vg+>A!E@`>-pm3oKD6Px^(fK8s*@z!97LcY!HC6@Q7zC;lO>)IyafHu=|qO`h1~{{y&ck;)UBy!1uTKMg$V zi-@H!TCDPZUo-(M>Zc4b^$>GPQME+niA_I;ZOT_chM0WflYvub;O4N&vwJdmVv}dr zP<5ut6PrAnDCJLr9%Ax|S(Q_Nf}6u8zsQp(HhHS9;sFgfVv}D1O#LbFiODBk1zhzm z+#EJ}h)RFOzL1!FVsv3a)l!uwHhG?fsMw=?|6IfvgeiZw^2>=E6*F$bONg74zZAH3 znc^0Yf0l=@qD-^O+(O)<_zT3XiXSIluK1sb+Z4Y_Oas7i>Rh{0@hQY-DQ5GlJzFu8 zQcEMjapW6av0c_7-4ui@u>m*Z;l)CfP>N)B!4DzIG>;SDE0#2%Y)8olWVd^Y!QrQ!e zyx~l(e`wkB))UdtqNj+HKK@zcq+j7<$w?`uu&{3LuyQhxdc};DEN>p_#W8OG2y?y` z`<0l2K5wp`>GsYwdm6-|@gvSjUw3FZJQqBQKZ?cAmME69!=uQ@4;ydMqG4<>hzh?nZV@pnuu#9F7_^^xacmx#W{~T)7Juq zudljZzs*2>3~TzX0QU9$Gw6@tGH3ew!0`3m1bysFsgHfW>AM!#*LN9+OTaT{`WT0= z4=-`aZ%R<#1n0;6-j2)H_c#bQw7^H#PW=06T)w_b7hv9oXNuHEJ(fN`7V`C#LS`d9 zowM{k0NU60%f%=UD55^j@l4-0fPH-fW&Ss-qV(}mjj!+G7X6k1^>NHIecVp-_5Bq3 zehHpAOW$i?`1-n9^<7}<<9y5Xae3hD`!NdZR8xk(rjJV^U*Ge~^>-+!ZHD{Yz8%oV`Jg$|_nm~kSD|keJej^~T&C|=34NbGL7&|w_5D1d z@13i3jYWMkJbkAj4v2>NU4=H(WyHA7T-!Gw$?a>^(#eFHEZ`@ja zX2SHbeOi9Mlh9W&$^WKEWWVPV`dU1F^F4h(Pw3-s+_|NSqzm5SewNU;4f>`bQA{7( zyQS|$^zVLs*@y-4J5`0;O&`y|FifNKyS`o5wbZu&m+9jbHRwF08As7WdY_ z4YU@CU_6@N*})&+t2KYsv-oh(_?5DnS;wj;BSZUbzGv1~y1!D&-&eSK z=Vc`|v!1Jd@74Tj_`%Ac@urS5AG^MzzU|0?myg%C@)vFVucY%k*7|Mn9s9%II)KUe z`#HrI?i#aw`B_)`zjzR=x@%WaYr6FMF%7$SHKqry56-%B`|`yl!QJ~xUTE5nG{3S9 zX}@}w`_-NOf&V3NepP?M?s>bKmh(H#_-%tA`ig#PT|uP;7#>>JD(znIT2yWh2c zaqDD$6?BOrr&>T%+|K5R{R#pn5kve#V57mw{?K-9y_Hq{lR1S z)quv*6I;_;i7p*KzBN4zbl<_r{8bD}duQ*Eh?4ApNfd0z281+l$QAHdPXb9c zLF7bkUW#(r35taPA)vO}kC1F)@KP`$sMso~SdNEdv6a@Zl}ou-(ekO-9#21&Dy_Ax z^#c)$_5VCG>s@R1N>KZK=lB1eGrz3t_j%uUy>neNvu0+^Ts7>aD!rq7c;EBw0li-~ zPqHX%554qUM|{aqh7R&89lJG^Vf+V5N$i(hw2y)jjvJ>sM@Q;K`8 zn>0?&+c&OC$qFBC-Cwv5rw-MedvJS43ChdGkLK_mo*=?s>{#yKnfk-Zlo|=G*`vE7 zWp+n=Mw&h)f%~xgE!}?Ac9hKMW5#2$7rnQ3%)VLg z9liQp)%pvRzY?vHc}ktei97kh7qJHFM9L?a(`dk6x+Q1_`l|nm`$nV7pdIUB?iNIQVq8;0m}s1Kz;Z#|JNFp>H~UKelXp9)^YuFy z>rCTP^*q>UfruZvQkDyA?6uJbH@F&MJ$E+W!qY+9a@s>T&T+l8J@#3QGguHcQ6=kn zUc<}qg$MiaLBO;(&fv)c@wPA%wC8_B)3j+^M9l-iK7?@*y*9~TV1kHYFF8H> zYMk%N4Q8LoID@5KV{e%Eqx;4T#X~j`Rv=hf0}eEz@fd_z${>R^gO!U?pdcO{HZ1osxkBT04eMsK>0$=`f{BB8nHg|=Xw`5^6 zPT$tsM_7`DH#c$HjG16@bZlYMOb+p42M-+DsL39r*p{XHE!f}0j0rX_STtq!f<^Ni z^>!Q_l*0(19?c6_W#eneG&s^SFMw7|-i3u< z37kox<8ms6^6q6h&gCQs9BH|i^>2>j7ir-afX;OuX41lE`xLnh|KLaq|7y^}CoTL2 z(6pb6U()20z7ba*7f8U77X3=lnN)xqk1LZCLvR_maw3}4wCLlt2kQY0pS19q9Fehx zPg?kF9~hoO{E{Z0G_TwX@ehu)=o5=RY0=l|$p#zFPEWS7nFBOik5>rIv=y55fzV7t zp)a-Q4A2pbRnd`_@cg}e-k)PNG-5}GpMiq2!_TB=e0$-SH1$a{s<~gmKRD82X9DO5 z|H1J`_>)149nxZlZFpWj{=tzJea5S3Ai2&rJ}h)_%p*BxTc_0^%~qA_cu3Qmyo>Pgc?`y&z4IH^bv-KWz!XFP7s<=KRD8&&wc>o#k?X-KI!Xm<$V?Z z;7E)9O`s(mNR!X#G~vo)gy2Yv{%xRh!sME>hK0WzIM?Z@k`{hDXh}EH!oLG_-U$4I zBQ5+5pd~z{$!B;T!6j#dlNSBQK=Untjp= zX1j~I1KQ`wxvQ8b_cy@ZMBFpUnOj?ECXxUCDKU(rB#&h|S!pr?{{s%i>Wf`08TRq( zQAWpVr5CO*(ql2?JW!-smu+u3@GZE+ zi9L>i+4cryIBQqHXIRAEb)ao~9|Kq85-0Yq2E(>@2KMN0E-t2n*!vb}+aAX=YrzvI z_LhNR+iSs#wAh1b*WOytw!IYqL&X?=#olV?o=QjdKI}1UyqInhKGp}ey)9?4$M*_5 ze5o)Z_E;~7y;pqpj=Btz)6S(Z~eFQKUJaJ<0&tTa0%5d`s z;4zF5M@D;G?rYnd0RxA@7bo_(=Et@-2xH|ZAf&y%xG1ANuF;`AxsTsUGW_DGN5^>? z>W$P0Wf^*94$U9R zoJwnoo>@K7v`hwZzi14%cw|Mz$S*mD`_vEIlq{rZ5+u94oMHhcby27xF<;gnuv1QQT=R!J>IeT(MtjXUi2Y}rJi4L&fj{>Y zdgJ3}bw~Z|S9=P7N}Yszb^Wm^r$3Q!l_mxUaZ-J2DE+dv{b%&ry<**4J0#8$3;Dep zCxlPmEhlKVZC<6!nWs$o$$PHdxdkU(mUPAwX$8T}6N2%yw7$3Fg`l`-au69zY&?zIc&44ybo z^e_s3W=-swy(a^!LPypH_lHzh+9RR(fKwSfV?6%AsB`j-cO zw0%u1{@`g9&n*jz_@%KC2IPdDWYljZsy4bnHY7*PC(>@4KzcW62 zb4GSX=x=$k(h2YBwBTsBE&T0Yk&;MTl~%CwdCT9{pS4|evD)RHg}gpCX8(qn-`^jK z#k1eR*{I%`=9%^3edq2QUoWZ6lGLkS@?Xlf{PKx2BmXH`EN46^dD6bNVI3&ztY5;1 zC-0fQdrP}6>8sN7`_FuC)dPPR=GHme>R5Vo&GYp?9{m(2mu^099V*(cPMmzcIX&=$ zU$9iSZLUN9Y!0IYcbz~9K9+gnOpa3@t%+fN=1ghL$v_v*{tiB#9w=COC~?_Zmg6YS z+TEnqsw}tsrqo=sy{G&&@!72EPOsgr%jgs70XqfwEHQq3j{bEA%IkMepu8UIf8s+4 zM|P;@)2^HMe)h8+EU_olv5W5eKx6~^{Rc|yv9%{Ycrm^yoZ*2Ply{^ob#@9A4V zH%+cQiPFRRWk`mVCX-P5O{$GbxqHVZwOwsn`S6Zs5-aul9NU96A)Kpv=GZ5D-^>n| zsqAI@`ecQ--5*tHDVxxWM6-^ngj*i(oAP|;qq~`VpA)O|!yz|T>(?vB>e$hh9~!@R z?P$Adzn)LxIq=M5&Kz=vEV=9=k3X$?M(TW>$9qHSnZ({e>B^NVD}3zjy|1 z!u>#>Z(;VyZ2bYi>>nDJss@7a`|rgstmDVNq;UrG9mmEK1>!A*>EXrKU@yJ~-{#YQ z#D|}>@Lc>&d**t{1vX;U@v|K&+y5c zeR!o0d--SXKjV{g-L`Q??tFRxmcf&J^0_{IuMa=y!_WCJ-*;^PxHp~PaUg6w9{8J< zK8MJ@ZQ;qlAK)^sm%0kXP)p9VYPRrfV6LSyu9tpKx4Aa1DA2VR40fbmYjY+Yu(=k%r$Gq_0nbS5=;MK2+AzH5%|}R zHp9Caw4EPM0sqdj_XFU&toxq@o@HT{N6%&G@_2#EFqqGPaRyfk#E3QTXPz0Cq5dZb z?|IBnJAvzs5dV$7>pkw~{S#cd!Hay@%MT;xxSVkY9}(!-d)tRU_F;ZUXPmj;TOVk! z^GO3#44&-M-|fR*`8N9R`s62kn0qMM{#W?$N+0GPhPFQUD75jj7TybiZSN3prBTNJ z-il)kMHYSqwDakXmj4R>w^;JufTj)Odh5B+_XWb4y`e_U2f_~n@7lHnbg*g@xw$i5%IACFZxAHejzXJ4k zC3uJ*$ENJ^Fc#RW4}0tHf|{)RS$BH%S8p{H_%Tb)x@3jr|14lTKWY2hmV7>Ny@i{B zz51rNUTf#oZ+x0O7sS_Y--bTjPF$QTzRSVnW#U@x!;ky$zxnY0^x+pAyiMB=tABU! zy$Cbs&VTRIKk49WwSI;QIdMCz^?U2HS|CD5dH;Fl43wGJoE?+b&h#_qk9k4^XP!9~ ztxvz;u@3s24ReSEb@UM;`ur2$th7FBLm#zKM!iw`xCfpNp^rS#r(@_NI7Vryp5Ug6 zbruSJGt=5ULPDQbq0czcCvKP-ZQh{|tr$hi`X~{7V#OHR^NvB$r-&4fq)%gXMD*bz z#pZC7ku*|l4g%2!jTD<>LPpX?u|963*f~0d2~n&M?I_lVc@*oo6qnL-Iaoy>d7_UY zDK-a<=wnFakQE~}`Z_gB$2eyL$j5WkO+LM2zauKRK2@#O%HwAgtNH1!AJmo)jLS;yu@ z@DGl(=syNp(uXwpOrIxl$(jh#qTd&E5&yvr!BxZ;61YNKLRWy!bokRP{tEKX$3M7@ zxQ6FwQqTJh&oy)^ei$A)(&8`6QZ8S1;7ALfL6LkREqt1j^$Vnhe<}5ya!8td#k&i6+?D8-b zd?{C?#SY7Wq%&#q^Ks3>CF=`Fi$3#O{FQvCzsqpt7U3TpY0+N^TJnW7`AoW1xbmF! z1*An^*C!}%v?=uoY1Scf-iTeFuFMr{O73ex^KlX{#&bdY>1lM|hA_u~@& zk4axfuIb+k{TgtlvuOK6(v|oJcOKHG$nkdw=;4)`t@uuqS!HOx4~c!g>kMb0;A+X| zU=1AKheYRU>Qo#442wU8xn`U*lvfHNIFo5>;$=)oZgb-aWYJ7F;7OcK(Pg?X@|K!f52FBqXK55oDVxP3= zvrWvMLk)AzE%|hlsei9W3x7KGujCNxj~)sM*l7$X{aUxujWND{-5xZd5fV>kal$Vjr8za4Zk<&%x@YxleD9A5p}+0_yrc9^CqHG zVd-$pGxIj1-vGMkc0=Dp{M} z4Z5h^&~K3M=&(&6o-q7Wq%q=%SI&oEkkA_{+q9l-RWcne~>lW72$s>u1by2u4iK|f_dohDeH}k{R0#) z%MtkdV+4k5Q)EjBossVas4$U9RHUk|;f(|M-5Qs2Qve;Ed_ zu%^OuF;hD{K9@N^9xs-q-UP?i#j4?AS#o&$Pw%s8CIG`3cn)dqr)+?_1l4 z?W)S+O3i{j{Br;9y(8KS5}pMPoE%@y^`WV}GmJa;_f?zl>wQzsl{j3vI^!dq##rDLgHm&EVWCA~;76v3&D%B-B7wC~EDZNn03YkT1x!Gr0l zHI|*z$#2!KN(=lT7OIJ#=}5h@@7UDfw4thMd1*%{zNMeEGEpOOerD7oDv@@3-|T7g z)Y#NeXZEe9gH_9!dYhI%rlPHbQlj^^tCU#Y&g_mnwSG@}EZ%lpNnVVt+osp+hHCci z*mc9{ocMVs1HX#J|GGWEP>$Hg-37#_Z8h#XO!prY5izmG{{m8X1hksM> z#*kl6czyCKTUL#@b6e+i$1B_rt#MOvOZyddUwd&|ZD>Mtd09%RI-YTkdNz@r=&J$` zg+keVLn&3u-%juPRcBTFXnJ5*&2%^a8|y-UEM3{I?3^#`?Dtl2>e$pO#I&f_^hC7( zU(}Gp(E+XQs^`Mal!NiK4CR%U59{Ylosc*(b?i0Ut|_?o-fIJ2ABd9oTzkrdpKUkg zcEW*4%#Y5J_GddKO}Di7d&^3PmqM|4&Tmz4Pr3@FX1AQ^voSFN>B2l3k{DZeq}t^6 z)|Dx-kh^<@d)zC1rRXM*As%`JhbhCx`y^RLA|S~jnU9rj^A|KE~r=j zdz_&Da>vF4JOC=F(2!X!h*fCGF&bz|sbAm=VS!H`_4yc8XksiE(wJ?!aRz(2h}}FO zC=xM3GXiwJg-bwtzNTq==ezQBRRMaeh5r%sXbV?@c7_c=ouk;7@%%eS_kle9a}`e4neIr51pmYGJ-gt+epX zpq*C|P+6L;bmiHq6*SYtxNLnd`wYhQQ|#*JJBYi)t_ICKFfK=jFIOPS*ILk?JWtOx z>~rN8s_%h*+QN^4o^0XogZ9FIq2BLcqa}X^w3q%DYX8xFbKC&^Jz%9kH9-9g^d%N% z`i5QXFW-aoSP{iTaBwaeh&ws6>pP}{psB0X&UgIn^&u9NO2Ulx(wp!(ruk+z29c<#)Pd($4 zb54Pd&s7BV$IA|88Hpf&kNNaJbMOmVexW|+4y1OBe_p@=_#qzR!~f{R6MXpV4z9<& z5#;AQ2cNI;R}|k(=~8iJ;mPNi37x@F^<4+k?<}sOW#^SkVyY&M8~@Lfxqie@#V*AK z9V7GckE=+yVnnYm(F;YWt{2LbQoV$WK1!%z7RpdhFElA3r8jrb>tXbg61`-moC34h zg_}U=-5e^&tDvdTTs|_2zM7RWv^5a`;g;KS&<;KRQDf4d7D$^F>GtEnJv`z{eV!8k<3T!8ZZs?bY0FS<-Z2 z^Q|-IHa9kkZR7~eGpKGT;jKxzNqS+<)LX`lHR?<{i#Mlb?Cd2b)paSxSe>(0g0R%# zv25pEU;t_1v)#;bntam2=euX#h4=?YTKKw7W5onVTKFSD=Qv#n(!%FES#E#)gCi|` z)~}Hmxu&iaKI`Qor%S;$w8*Kbt3hWvG~dsMJJm3sY4Mk|*kM$2^YIUkwD4&n;)I{H z@IktJC7egPfE@G9Z)Chji~dBggil)dbIEt&MOyf@DSIw3oU&$tVas$HaE3?t^j_97 z3?=_!{DWgqhLZuO(_xsQ*|ualjp!oK!v`5Y|4aUomhkBE@Kth5y(fI)vz8xb2%KGh zn3S@&0%@_sy0d6F{=p5wmC42djz5|Iph>+pp@@YAZaFUD-w%4Yqr-kegdHY0(qf-> zBK71Z&$%&dnZ=r|+ee|9o|z?v-wQutleF0EOa5e|Lt6OSrUO`%W#0id6SD6>Z_trx z_yvGq=o+^=*ZW}P2TH)aVZ+D*?L}G_EWeFEScA(_cGsp6ZJ1O`ro$b zM@Ww`{EhgLdHX}4hkwoRb)7Taqa|#7UZkv&7CzH0mjQtzEqtb1(La%EzF8NV#X7T+ zT+`3X8RyY*FZ))xRfbPm_OlLEk;<4>xd;dD`7-Wt+FdI31PL(fB(oon_PB}!=^~-Hq#;XPrw1_ zm1;ctE>#@q<^C?dBs=;8rS?LIhFibHE zG@H5*F3MO>&cS8dn-Ak6-u<1jZ5rt{eo?85e)QHPA~S^t4D`qykn2u zRIxYKXYYR48v(gE2_KK3vBS3>_E_E+K6anQ-b|mpJupxq#_%iluJze_4EAP0PJ8UW zi@n=@_CA4u7a$iW_HOanOGPG}ffDVpP7!-uK6^X|y`Z1iUl^fJSy42}b)zYH(y5G1YZmgAxPzI7vQ(eYWa^?rzS>C=NA z+8un#!53*WjrK+JXJg7M%D(LUSs4ll!(-x$85&iIc{9#yaNaDW9&O_cT6)cj<=wfQ zU(>T{_=TP`ul8m1+V)RV(E#V;8r3HJ24796yb!-p@5!IP$M@i?H7a~oOTkwMtMs!< zyT^ytvP9pRRpcxY<5-2gBEtJ5okp~0g^&j4o%|cEtPMr`kb3nq@IVFko&0PaQu+9D zF$h3^6XZ+p50<9u8BRox2-m$KJB+Vl!)zWjo{r^?lu zKXkUPiFNLcWjv!`U5!bJr((`hK?FVYYhiN7K;D~(>yR%r^Y709|Z1P;| zUR)T12j1e08fV@@G0wFeyYG1=b^&ILECzQe1eT;8k?D0M~oI1WT<>1{lCGL!6N$tj!oPRCs7?L_}%*!ce}}_E5gA=^>@P*fZ~EyBd;0i5T{?KXDYHJ72ZcgfZXy;&-B%r!a^6?q|^~ zy|2*9k5qcN{^vS81!)LRO02c^*4ow@vGLz;P7)2A<$}H$z3%&@v-R^__kGj-bC~Q& zdG5c%oTe9Aw%<`30<)LoxuAXvv+*zwfC?&SAkN_il{0YW;(zezkM!XZAGUigv~Tx7 ziAVYLodFn!8^nnn9Lu=C)xx}PBH zUMcIRigACgYcEH~ps$4)9xv8;`izlkSDuF+O!Ay!4X|hbLOmxNaP=?L{(Jq_0eX%W zT`|WE(C;Ec1R}N^LSz5axUcxaKTyxvdh)1GK3K(ZqbI*u%f0uGVS3JXf|yss^qg&_ zg_}Wp`SDd>_=l^TA@{;RT+iWp;UBK`=eqVr>i6bLEW8r*cnjYN`dSOG23_gm5JTr{W==WC39n3uCoUMP3byJePK1#m*uh;rn>aZiH%}iYK?Z05IXQyMHMUa|* zb+Cy~KXr~%*qP>>qrKR{Mn6ySdmM4JKh?o}#v*uw{Dy;>=A2W#-NCFUB3ScrkAsbW z0~Obs(;0bG{accJu-cI%zgWHM;7PbAg7W^ZgIQ-pSU(17&E$Cm^nVRolW3NIb9Bkq z?k=dgr7^y=r3V51H({e8nQ-MKwlju~iC`4jH{7&zNz2R|5eIvE+=`Q9!{XRJ>cqEHBrSDt^gYZ7OTo?^ zpi6_r!X$||hUp|i)DW#GRp`^i_S>UIa3UuwD4&&mkkgcY2mZo zjLarCc`lsoVW!hnEFjIw7mih9k<(>l{gdf5l&sr@pKsBu<1!unTF}Ft?j`N#OvOJq z(h?rlf%1JfY2oX-+zBUX;d9KZ$mya|U*>c#06m-u0H@o@xgIU|vVF;M=1oZpzXG&; zqfJ`)mx7kGA}xG|U*>n&ZnzVjm1Xwv&8$N0Ai+Lw=u68~$2_(Nri$23F-%az`DXPRjI9;}@49&h% zrb9EGh5p?98f)(T()l&c=PD1^HV*YL|ViN1*! zX42x<5YRc-8XeNYr@y%q4WG2|M}n3xlNLVnsOSp(gA3!z`MM^%*C&$-AV4{b0_!yzs$Q^-2yZm=3_24OuB<)x9~lPd=0u! zL$}M_ZMrRT&my(Kj(IKdVG=Rjb}?XZPe*}^>CZ?!+avQujO{h zd z23Z|D_Vs*Wx1>qkzQ2F9hM8MULH)zJkB?C$%Y6}SJ?>+`G!!THz6w6utx88wmW6f{ zG~42HaZyJ5lYRCc0q!XtJnP=JHxc%DKkZ>@glli9&mPy}JIy7uy!re^K6_Wg-Yt;R z9!S@oe|^Ath~Rk0#Yueq>jPef5$e%iHZF-TzZJlr`+Q7?fu73CR$usX(OD6D_IeGD zgW2}DM@)a%7boF6=(9Hq_69+T;mg4#;o}~ew!J%Gpr`nL;Ir2bdy;;6mc1O@Z`*qr zxCC->621rsJAcnu;lp&i8@@3~wJ;~pJ@Lckv(`T>#Ea7YS*?YwbU(~Y4 zH5Yb#uRlxp9`o6I(+b~U%O2PB*!Eh^5q4iSjG6x?rz?`2@SO@bNi&iL#-1$%=aVR_+Df!G`3vxnnydMtkfp~rhF zanZdDd$aIMdmL^MdsRMrBOu6tT%3fD`DDj~-{mvD;zW;U)7$=zL}l?Ll;|((eerjh z&)(AzREshEioL}?d#}UZAjoNt&#~Bh&}Z)i1fwArC-&C*?45zV`B0)g=8M?-q0e4X zsONNf-KSRpy`G-8|K;=dMkvYi#vuyv_mt1xMA+-8ynN`h_cZJca^k_E4Y79t2s_`F z!(Jxj;v`-AL67%TI)YbVFBde^g~J+RkL$2#PulR+zVJ~dm)HhN?SbASX&CnhJp~u- zF&yc*5Ea)opmBcDV$c>H+eC}4-beUqXvA@%$6@~^`fH{yxh`vT`Ip|;U?dtg`eX8A zD850hI@`Le{e!s{2CKA!uZFM(z;Trk3OQ?*ux1Bmf4sPZk_=hLg!Mk6&9zF@5Pj;c zSZ@ELTlJ!q!otW0fIxzm46=6v$sJiLf>#;hyy5b>zJ&)BDwPC#$>Q%kI z0wL1tpss69h=d5KMP9F_dVQtuu-4k|(_c1olwFhHd zEmG2sbt|j6D?|m>vJAo5EY;wGx8?-aMnzYgQ0cYViK(i>{W`uY?faia?>nI$h{Z2F zt)fq)I{QHc>deVrd8s@1e7fDNNAt&^r}aexzAy&xY3)a;oiXlv5DlDib7NaVryo|k zNO}fqV>(TOQyzsAE{ljD|Yl8#-37n@E3;h^Gb@B zi?X`RO9yXt+3+6x`10i^$q%eA>dA-l9(?FJbv||;>HGuM!F7D35-XySqbgIc74j^e zZDrWkda&n}y)RpWYd+TspCYW>I&(A-P2H?ErEWL5|0P#C>6KSl?R15;+UahVfWLNM zF{R5;4WCJ@phDT{>e%;LG;&JCS3ZH0hcfq@^;~-W)R$PxbzKj!(rdNWu{L2%5>ae3k|~;wIT3eA7=00b3v?iu-)Ik7OdF5 z9sZU2aJdh&_vg8wV(-m!T>E9aAWpqy4tVd_Aw8#n?!Dtey8p@f7UTNp?+v^@UzW;; zfR}MuIz@&G#ApHEcQ0{qKmC3&-NKw&oM>V8hnBfGN5^21h1rLkV`19&-kI~%IN(}W zo~Qf8msb)kecI6l9bCA4Jf1wV)XMceHe#x^xK+VRzp8WxOj>t=|uju=| z^cbkmNy>Nq8>sh8_2gzhlp(Hsuv!Y*%b&sKoB>yUv9>?R#lzJ909|3>5sn>0tc4^t)8;O_F;d;VnVDf&7959J>7QCsiHO z$h_}F@b%iq4ra~8{XF`n7zV@0wPG9#qzmJ+*NPdOp{`7lduzqa{k>JIBWD_Nt=NMO zzDVQq)HVkj{XXiDgPF$>yt({`g8}tB&mSDjGRn1K^0Ira#{E>L_2R_mR`%PtMay#( z_j?pa`6LHV0L}e98XauxU8rtLk`Hj#hMDkwMSahavrI%VGX5_PHsOn^oepN1h@iIM z7#iILxFQ%Km$hM)8V^&a9JvY4S5@yI1aPbexK`{U2Q%zkEA~|fkJY$XRXf=DSL&_} zV;vV!<%(khbmsn1?%FUD{xR;_FwuF;(a!*bYr}r(V55JjSu5teG;*z&Ug^aJS=`lQ zB-e};bH$ckI9AH_TKaSyeTr z{7ie6&X!rTZ+^C{GxBCCt<_8})14}F!(dGM7AtA}q?st&J8Oza77KNo{lwf8z3|1{ zSuKXJ%8nlTMmHUtyIc%k^&+*iul}N{om#NqU)JVVaiU_)0P1yGj^Evr*j1DTV^opk zc7-xY$yjR}R`R<_?rfxJCFMttS)uQBClq;~JL^cG#Ys8k#3PAkN7+9`NGnM^N7>3{ zezYySu_R@jrPISkF)SRqeO!{v4^SGo0=n>y8|!Q$t#MX{=-9j1bbo&jlTs zgMVIWP-HTJ)22K<8~E%)kl7^&HM4Y};e(E`?RXL3ZhwNCg+%e`z9WW5n- z;nRCLV}rEt%Ro!`NeiE0kbOIt{zXo=7`3q*X40as)3yQ(xO!Z&u8UbXoDBus=Z5(j zqth+SPIvoDhnX1&r^Eb@hSurHz``*vB;QF(_}Sl)^*yA8Uj#bWS>r`o_{;`LH`2mC z4|EX=2%Jt64yeMh|0VvC7JZgISp#I3L#9J+CH}$LX~JS5{*o3uEN?|s_y-rpRWw$U zdafYTp_wKU2GVlxAkea&kF@Za4!Ps-56(^#=7og8&X)}Ei>j&p1$phP6Sd<-JF@o6 zw)wf`vsQa$@;YW{o#x{W%`nS)FBZY!6AZr>ekA;)CBC|xIP1WewsKZSC1^R5<8$-z zdMLtm%g0HEZ^xJRB@Cn`3_2hG+34%=)EoM2VgB6mJk#9EbP)TxJYQ+}-OBT=hTp9` zPd5DjE5D}voqkO<`u|Dm($H}E7PIJnjNO~?7Vx+tyZ1l~?h+rj(_}RCnx^i;v#@n{ zpl5S#o8O~XR!__)_qXUivDFs$u&hV-6YSm?x(U9-vZdXVQX6oSvisex=yPthle%XM zw#HcuSsk?QHZ0-i+}^`FG~TGQvWFF0+9mF3?rTeu& zceyt1_6mK5-43FUuWw2!McpHmBtDD3NxjAHCX&S6O<3hr_ba_(tixxC*`@SF!IJ7i z{`Vgq!0`^g^0VEfW1C|;wkdQ2aZ%5CJ?*AP868I~SzmvV9_wGfzw9H46Mxwp*m`Wn z*iMTRJ@VORR62qKn3QE-m|c(%F3Q-x+=R=v*W~c=qn+5h3&_^{CUAeFlBD;2pWgkz zl9p5ydpkkf_BH_br1$SWy^UwlJLS`R61b=E<-k+h-#yS{n=4Mj$8$z(y%SKlNR;p^ zdfYG8){6l2>OxC}$3BX}A1E(J|{Ik+TzXMFZJ-o|G^oY*_%v)3Duq`$P6hfC~Tgd3ph zmd*9RJ;j&%?%LsNhdl`&>lCq9>9hACa8KdmKE1ZRomTklHEGxR>|KhArl<19^FVBS zqx*XK`xPsExBBeyThB`%7YDI+JX6lLR}FjfAz}Ir#3k`v?X!0;?DbUs?)2Gv+6rIP zviG#l9>)jSpBD$Qb{rSA!}n7we1oAO;roTp-U-;dz_Gy}vA54>Zzu|})TgZD#oqfq zd*?w=Nkfhkd&hnDDq&C3kHa2fuNT5&m%m96^i*Dg(BnOojz3Sqo|I>MP1;bOz4f@s zc}3NCO8BBadxOvz4T1*q*ItuW}5J(;E&j=^w}E^dy;;fqhi=u-(2Uj_bvou z!51g?ST1=_r6ZVSg|8SqvDfOeHx!LuPvzw{pS^b2ll&;P>^uy<^%>A8IX&U_~zpVyPO_4i@z5_kG3lv1IsOcSzn63V;p`C5u5@bZk2~1?E`D{?qcmF`zrQavRiWL+!r_2Mq(wkU1{o5uJd~!wh8mS-0>|Md{u1X16Ati1A)G8z{Y+}7ir@G{XJ4tY`h*{^ z^mOHfQg*dH+O|!fvDB5;S8*j@A~k+`yE?QkJvF*~v*Ot?3wB)Az5u5J<=|NkPQ$Ke zIq_a6165f$c*cTQBe)KH?mp$6@UyM*)2nju^l?`w?#_nY&khdmm%a34pX~C)dD%7n zxQYm?q@q}gy6e!biwj;Ea%9rs$uC@cApF{dm!}`PeqTj<$*QfLg?F}A$3Lo0dnWBZ z>~V*(*x6T&P49XzEv4(%87bMeDK|`WwOQP@XDSyRE)p>b+9~*L+wvXTp&QV=v!!c@0wcwflCb?pl7$j+C0c+ZisN zF}1b5V2?@H{mT4qcYZgkLY!>X75bphkObEzhU&z}`1ylF)a7$?$K|`+RZvE|zztcV zI)35WKtZ+WZmYbvI#72YwyUNpMRvHtiBvetNGu=w%HY3Vx1BuC$}zR}j5+5hP{&lR z(yJv$v~Ow84yyq6%0j+mhwOaO@mRjj#AA7fdkRq^mba@lR#!u7Im|b#^SZa1Hd|w@ zFXkP@YPJix%iuKCbv*6NrWGf#Ga2`^!wS3{hkxo+@SW}F#&B|1-*^5Ngl6{5r-Iox zq@`|J@s9fCj@H=SyF$CjM~%@t5zD?gt#`cFQKD4rRTeyom7BE4IH>Ew`-bcua9~S& zuln@8k^;H3F;{9D|AM}Z*om6WYB?nDx+NLK^UV_sbRok>0JU@f2=j57$ z!D*?=TlZL{vBVfV)8(E_#dC*v1g}EnkrTh*RJiS`R7DP-E$$xY(bX2+VA1U^9lWYt zwM}nO!Rgfq%^-v(RTWfYo$2`2m{;=Bbva<}Us>}XJ8EiP+`(D}?GEZSFKjpkEbC-o z9YSKx-g@}w!A)t{lPT!zqZpbUX!E44`-7We>c#C`YiUjiqfMj>(nQWb6WiD9E$nQo z4ym@fy>1De;64e#Y1J;5_L;*!`gwfKy6`&WMBCnwb(U88JJg7x^|S9nx_$+#ysv^C z-hULK&N-#L8jE2(QY|NBi zTkaf@nEb-@L#)YIt4(-$(&4SEO75&!C9zw2F!j2^&QJGjsGnW)z|XTomB9^DTlZh7 zSCz*@8EUbn-l%Wge~qT(zQXn^UYw2RU+_9~W!i8~T>QRtq*OY}A6u6b>icM2T2i7Z zC(ybyq}){>jZ96+4z;JOIoNB@;@Yl^>Y!Mdp15o3nuFu_TnTPXipP!j&QQG$EUrD0 z=*rk@@n5KaVDI)e9c{H^lWmD?ZlxV0rWmBjuWyYS3Zu;@mH~Y-D4=?Ab zZ?`TUa%4;Um>QNMmZuT>wsm@|+KYE>t3mwFjb@%yKZq5!MAP7@CLGw>zHL>-s^{F&Qnk0R-D@ec_6B2n)uXZa`KMADf}r(mJdC_(a7qdCBO7_q zw>l--ugobiJQuHE!o3p$mruYcd0x2@JIDH;^9kz=Q{!!j?LB&U<>ZyG;jG6M>lW|u zN|&+0ew(S~24W9Quixe5o9+wlMHyiWTCUI2Y&%+mz94ZUo?iYgMZa(pDAPCeLZs_a z*dMca&I^TiT~ou84W$%cf0$<-q7J?W(qGQc{C;RjHDPlDlpDFLujxR)~8Kdry>Cmc^F%hn`Fp_I7nk z$eLdSH+86ZTF@!!RVjfsuZLpY)&G?1^M-MqXe&F^u?vG|SOXNi5`N9JBa>f9uU?F{ zk$vzHtFEjT-?y}1caVOvB{W~-gkp8o@x`YyoF@T$$i^?o=(S-+P4zF2uW9T`)GW>p z?R58`z2l87omW&3KbRffiC&{vZMzCJ*~&Uqk%+E2t=Le(RjpH@m!_+ddbz$9rX{Bi31{S$n)E6{mDsy>Zq*{#eAneX19}p*hT_XviU+ zdpUVe`0$p_idEa%z1E_z{g<&}FOHw)^$GuC^_<7Q@?xyti*2=g)Igr-y5`+LyiW#d zxyMt2(?cm;;lU|etafZmM=(~c;(gLp_6;{+Z|&c@?@p{4%qg(hH^kI`Y(J>`t6gbN z2jhR(?B-rLblF{`(exYzxNkV!a?1x;x zFZ|k^JzMWYoq6YT?U!}#?yPmg(eGg4UGcsdf$WWeEKmq76Ga{_sL7uR}yviKe8fws+;JNkncU*qX-r2gg5zoz!ETh9%E^*bHy zev7a4v>&7PozT7#F_L_eKF_wx3nm^~dF{%;HDfP-EipN9WWt;YUJJ{S0hU^}T$A<{ zuJTI3;DcK_$GCnJwh!JV?M%FHy5bqYrgcA|{#ExRx)SqHFP15(Jw|YYBw!pxrC;Lic+Ux{yC~M-tI~ z?0fD}UfDjJ08a6+*HdGgdA8mfWel;@tapsddeJ^ncv~eUQH*Yx(FTr^nHs}ub-#w* zlBDmJ_TI1RG_J$`^HvTF+a>*}$DMY0D%za}9HuyV#8z9;mY#tiL%#T#E( ziF1>^Ft8rltFmHSI_mI6-@AbgGq>R#pl$CBDbZd(Q0p+B(zPNeB%*x}E5aT`JwJ?{rV54|JtH5{&1bP@eX|9mjHi zdnOyZ@=N@7ZKt0s--T{lEHeLZNsT-&*=gznjFhVAiZpfmRl<$7f`j(s!^*W7F_w4@ zSlqQPlyd06%EKF9c;e9EeMk1Zw)@vRum8=QH|GBOrq`QZ+to3mGyZy7gTF7<*-&*; z;-*`_RoLEj=PT;=3m(tN?)@*H4R~{4V`)8w&xn8TINsC;sddh~ZNbVTZeA|fn4OlHE|U0z$1iA$^;P|1@%xWs z#6jZjQU351wy)hW24}n6=YYuw-PO+7PI)!gT82!pV z)i-`uZ)M6{nwsnSSGA)vMm>(0$$dZC(bX|X#rvNOw-vRkc%pY^+y36_?qhBHi&Sub zfBmhpzK0_?`!6-4Gq_*;!>HPYr+V|A)?bK^!M(lJ;_Y|Gq?e^*hkl0N@3-LhRP^>v zs{Yl%y=iWk-uclpX^E8GX}j98V)2$!sa~(1=Rc(La$rypIjvuP{`6pB9 zqj|qMwkqwt4dXT@j;&04Z^gRdk8DptJyC-)}HpR9zj`t`dxe6;QK%8>iDhV=%Quqp0u~$7&m)+ zS4ZE}W9cV9dTaaFm~W}fw`1on{j{s&z7+7!baj-a96SG{ek0SPIICkp%gUW;zj(QkJ@5`@FhD+UuIjZL9N4_P}kk( zs*}gtHlM5h@>1IiY3k9sU`>n4KDS-H=jMG|4Da#5$5h+ZsVTv$f2&873Rebe%G71M zF*29(-pY3qm+d^Y+Ax7D^_6t3FQzNsTrd)dyp2~U4h3Tz_;YxZZ= zhz%*#$9jiema-tSV}a1iZM9|SHDm_k_k5&B`6hLUR)rgGrV+=)e)*Gin2KzBp|?7B zyO(alTT}PI(Kp6*9&N3`8`Zk>?Zaa6^~ZZNeR7aK+2@|Ce)Z?Ji}8QImp)xcpRSt+ ztL$a{)C0%=I$wQ*aN`Y7f%tu@sKJNlN`T@bPe0Xg2yfnZ=SDMZK~^`7R1#)V1xZ))Za${BA!+VcSt&W~anY zzl;3~l>08$`fR-AU5rfNnK*&ju)Zgtg;bmV7;>{6rKLe}tXBL;e||vhSmOAhV(d1+ z5dEO$XWPsDq3%fA`bKQ{cGP|Aq2uPsuYRhZs^s`Rwc+xV?Ks^fNptBBYcwatErs)b zS{IA2K5n({c=mVewAr-EVx#t{JATfJP14(qnh0 zt@o_l{quxZH@mf(eXr;qh!wg0nV~L4DeILrFJ*^SL+Q%>fi-$gg`@qF1KBlos^2!e zxW&eeyCi?;Ej+9-KQ?Jv{^YtT+<@Ra?Vl2$E zTrg>M1m#$`V7Z`P1>!M+Nvm(DZ?9$|wpZa1=X*DU!ib9x5Axw5KFk&Io(m?ePNKac zPY5b#ouoiPFlqH5afwggUbR4Zg->p;MxoqZtwGFH5uW2WwzdnpD=7>&n6z4k_gCBM zz_mU+-iPhgMZABaPwsrc;BbTb3m;Dw!Gx z5|7mK7QJT3W43stmM>LxNt~s6&nB`kl>+~kCP{<%`hRv3XPN$Ho-CukEJ+%~X(+cR zac))}4mSEXCrN`?b+$f426k}IvdlKhXwadZ1@S6g;?SlA1|7Dv2{A&lN z;5S8o!S5*q)wk3iJqYSs=y4lo^uMLjKzOxAsyc@P*M&7^Sd25+tMv@d6*1~p-k^T>I)FX<1?n!~A(ng%aEXPJ%gZ1gKd+Zzu;;(QUU&zo`(e+<4<-3$^j+@| z68h)4p*~#b!$m$kLSwIdj8Kojo|pbc?xp`ot$(%ap}`G4yvT=d^I@+%k5o^;cdXHE&OBPJr?FZwm-Ho)5FWJ5*^;ruDnF;f!xO26JvrU=h}NO{^tH_S6-&= zP4?lrKHToZ_ga{1{-3n)8^BLn_-$aX)h$!M1Afqw{|VS19#6kQoq+rTT*g(XPe6O| zt%d-p zGg18p*o*%}^;_W8u7?wK_3UC}zX?RmHv#fWOU`|z`CXdmPX}fnL+}jX85U;#7Ks=sIS2Ap z7QPYqQVUbR#KqUDCdePLF!znFv~Y6ynW6K;%fA`wo6sNP>Kl2ki?3J9A^)a@R|0$C zyB?uRyCC=Sf0p7mOl|d-c(c z>es+cmi$fN{uX{0*!KT@U@yOJRNOE5GFSg5^)c`z7XBOXc@}2=d+9M>rKZ~T+kBM) zT@!LMkGG@y^@?me7)TDO-%7Cr^ybmAn;Uhl$fe&Y5m6UC7pAUcF!$sj_ z{YgIjO&@;Hhj;q$VIO|Yhu`<%3>id5Pl4tCA`3HZS}gsc#1dTSb6@%gEnEid?Sr>S zT>{KLpK*&+CGZ;paepoFBnwXjzR%L13|wuM$E$(u=jD3fk(Rsx_<76TO~5-X`66KZ z`B(a0!x01Ts$j?)&ljNCdi-WJ#_ea!E9So>v+x9t_eUJ!7?%o1sc=K^FynpcNf9%6q zPFk@K7eP-h-@)rO&UMrBS&c7Hm2TP^|0g+ks+RXx*ZJi0eE3@q9{TJD7D;1U>I>IC!_lm1>290d@Lt zEf(D@ToKGS{;PxcXk4v!Ie4expzS83*$xg7^FV4ra1Pu257qJ+>JkU9)cATe#la>$XQ@RFhFGUhyAQ8+ zaFf1&j(XU^B^o!VfAi_@bg+rfT=grT{5=P6(f8k|K6CI`jc-y}eDL7d2aI4BfvAJY zWB%3o@H`)Gcd$wSCUv(@{pr~Phd2805g-1m5BFh3&Z*|G1B{-XXeJZ^X<^qxHtj zCETf5Z^&Fm-4br!T*CdHOSOP$QO1Ae+)28O(lScRDJ?JKKk~}SGy6x=qYBXl4yI2>Onr)=bfBIZpqVFov zcJwCK#X2IzdUNaIGA%9B(lULQj!m(SNwJPfv5s2tXzkZ%?eAztr&tHJc#KYwk>r%< zJ+DiObwW9*N6RHTO-po0N=g|15}kx4&i321SyHa=D%EyMwT@1{5}kA<6($?BPKDMV zt!W*Gl2JMgdK>N%9nO-`T7L{d=-}FodOg%kWa2hNYt! zwo;vvr8*5ub!bb+(62Hb!ZIDgvXM-~GF=ABiW!$O9g;Gg<7L|SGM$@cI&I5z7|L{b z%5=__newZ1woI4IGE-7@T*`Dhmz8T@%C+z13|pBlrR6$J$||(KqjVu3#k4KcaVgXB zDjUU8RHn;v*(iOlF3n}Sh?nWyDbu-ArgNu4=T4dPpZQXz)1+)P?Nk_FM(dD_p^xRd zoK)z%9;HiunSMsf^iJz#W6WLjw?bPe)7!3>>GUi!>8Z;=xqha~bxEi&VbdvJu5Ff& zq@8k|+T|meU*)<~kJ2e$p<`FB>2h5{%5|Bk&^cJHbE-nev|N{*a$R!Db;&8$xmK>r zOt~&I6*_&&^>**&Iv>k*K9=iTE7$o|u5+_o=VpaYw{mO=&(zT2EM>Zt>mBRM^;2K2 zpYHOqk!t?Drf*b_oyiR~Yv(mDX;I_H)y=GS>-upE8dR*Ro;!cfUQpLGuZ7jU292ma zm#8I;O$}3}8m(D|O5NEJeaQ_oXV0I%Fpk>Q<4~IhCO6(NZwc;q8i5{II1RYTW_wkC z&s?p)WI?R43C#oQd9*}hm(DdlHv{mli6wC~RWPi>?==V#qb2HwvGW_pFI>_>1vRl` z93tMc{`A@N=QUJSb1QdRX6iYJi8ZA$uDw{I6HMs;-{Ql!s}wrfg8F8g3%0W~!sjY9!C~y}7ZmYH7=j6OolpS~o6O*nI2Exe)NK zSYu0d<1*gTxCD{(F|JtHAX;^kFL%UKJZjzzP2(0WZGy*?)iuSZSR5TW1b$|t&U?n* zBrF4?6M_4fGG;o-n3_}PEod~!BZY5D3rmSp5+^U5->7exSfdl)m~&F2zIkCBaiE!+ zWu`0|j#Xk8E?6`l$wawhlG&q{ET~0t)X$#RJg@17WdA24xt2BRvdna}i*e(E_@Y}G z?mae38A&jG3`vW z4SP`xj|`G!37>+QRf`sli|K4+nCfr5)mC+Uu36GTBd%2^-6l2O%u*o9JEhU;vN2gr zk#m&2-kii)7ha|i!aC1-vQ24oANMI&%tVeh<0;Zj?^)=3!8C@z!HbI zWa0c}jrGm*ma*5zq`nT_8JOq6+9>Ljj@SqU5^)Iu=QLUXl zZ+=x%Lk&`xDwCEjsEW7DTh_=Y2VHn1?Gkx9Tv@U)mLER5yuEwsqUcz8!si>0E6bBk z%m0tOHvzAzI@iZna&i(984f2vP7pCB7%}&RW@7SM8XzZu2wX%2r4GO$43?Q_#j7@u zgvp^mM6pFLlZsqxm8!9}${>@dXsLo)wP;kdS6j3U&i%dbTKn63ot@b0y}#%AKfmYs zuVnA@e((DBx8}9ip4MX2obHCLea^MDtXkB9O6>Ytq@`xcCGknKM^3taM9u7JwbR94 z&n7&$a@f%6lMvCF?3?JtNa)T~$Hu)3&{1%C=WLQdve{TCFVm+>)j&%!$uoV3X)R_! z51hW{*Tid(W8T?oN{w5^odoON`r4_N%(y|v(pX0laq1x_IgG4eI65v;nc0y`J7oV; ztBXd*9l1WLX4;sVxic3^wjgs`E4do1m?jDAAm&V&5g$C|x|;d5GZvt$yJX?)I4UHH zWX6K&HB%D1&F|13fpegv$a;saiv6p}A2gVulV--f)?zXUAUDkLHiw>__vw3q9j#zl!f5_rZ zGB{%4dHJ6GW3pxrMd5jEoE6iwzRw_h7f{*G17>33`OH6Sn9(N|z6Wr&vkn2V@HxO) z7hwmNgH8Bez}Z(}2S+S?Z{Vy^WE0m&h(gZ3TGRTTqv#ib${K2TV&ThxvnLv!Soi_J zS%VEvEc}JQ*^>-UEPOd|)>Ed8h=s2Jj!ec5j#zl+RrvQt-_AECDg3nIiAA4z6^&!( zI4KK?qLTugJ;f*x3r|MMf>?O1UqdFbZcvu8AWqPqN~R5cr!44~pg)aFqJA&P;b%=6 ziA6sQ9GZ?DTm`n!3=Q>M%Z1k%d>MWUe`0VlezNu?>9#^lJ8U=HA~Ugrv)d8vXYDoN z5=*$eHy@dW9UQUnYvo)>Xl@(s6YhUYh>AL5{Q|cb$rR&}tW5=$0 zY=fb>hUY#y&){+R3GX-di6z_=>dZGfj6-OF!MtZEm`zbIn^V-$VRnaVv4guETcPs_ z!)oH|$eR9v`OJFS4jaQI)_sNZNdmFNCj?A={&FMFUmlZvJ$7)!q8|aKK5Y<_CuW_` zc0NfU7X2dN@be}gh=pf+q#Zh9;md&|H(&=>j!k&xNqCphCl;P{Ozaa2&%6@*#KO-6 zj{G}zaKysj2rTxAgV=MfbhWh=x zP@TcdcZoBx*kt}k78xD8ELi8mFPc0d7X4gc+To*IV)DfK*s>R62S+UW1;A`;teeE- ziThy7uE!3JSoBMP!v{^c#KKnsOZ$T^nb7CJZ?IJ&~epgeMlhz~YI8 zFSB@J;WL1vw_pdy@{QiAp>8|T+YIhao{bxh@n?SWmmBTyHx*mt=h(p!+xDr?cFC~F z6L;kAO6=f>MW6YQHHd8MHXd1JcpfkMOuN{Ze4u|Ww#aJi;D|+^aBRenJ$)5WDRz3#I_ykixXb!voXPaZ~eRehx%*(hx&K7)|Yn8HYn|ynE4<1h0!OL zbTJRZubcGR`Pl<}=$A&H`5yk0(Xq>j^(XwM;fcj&Kl08Zp2Whly)bWCkBG?=S7VE; z!w!yE^e+PzePYqS0yz5~?BIw+e+saimx+a+4xD{2c5uYP&jqGF>jN=);#zFk|A8GG zvFP6jOnsI=F?r%TY}xCvgR8?P`pbZ+&omN~CuUR3-hdq(vFJYx9Ddv6C$aE3z|oD^ z!4+YPZqiVd<0rJ);L*hQ8N3TWh4&l0*TQV4QZCYFsm1{r#&UP1hfEwo8ABoQZ z$TDsu7XCuu$QJD2h=uP49D0zf8LLOP8eD*%kb{Q+3qK6F@IQ@??kC?dd1CjI`S1?^ z!SKWq7V{^}k5%A^g=c)iErz$_RsmkFcZfxwafm#G9UQUnV}Rv)idgt@z>!~J2S+UY z6~N(CvWazdDr51v9CI zj~kxeAxHmZ@(yM_3_Eo|&ILSz`7GB;cAhgoWvpW7?QHP87Gu32W?G2nV~ae29UL)v zx-Hnk@0+v}3;&G86AORX;)#X-(Bg@OKSAEPrXvCLzcrX`D)f}Wqwo{`oxu}{onx;94*lNn_mY3wV3tLciG$mZEwo)j{Y^x4hrwa| zNVyP8JbM63J`VI#KJSLXot?uH|9g+fTs1V2BLojD2YEY zdB%Stw#f6?!A-;_`ZoYeo)8P)9ys&@c5uD1MPJlVU%$7NUY3{CA7Zgn4lMB@miSZv zM-E~KM=bhvz!D!~;g0MA7ZgTn!MAe5ewf1IQlAfa7EbU zSYpw+io8>wiG^qU#ipH>jpUvFfmrkh07uyY!%e^zJ))u7il4&Q4CbEYmF$Q-mRQ&S z*9~dc{}3p-ej%2yn6AhhMxR*t?!b~3V&QXv#SXFXESJciu!AEOz5rO_Nlc#c?1L@x zCU$VdqR+l7{F12;#KN z{%jQNJnR8V%AHtjUI0uxbi~55--sN=4vtuO#z)d($E_NC0=N|0&oa?_vi>Ec(-dr5+LsKNncah?qRfs1{qK1v@xm(Z3m3?xPY5zZf|39(HiV z!ru=p_KC&*R^Z6{*ufEt{$s#mpIG=Ofg>Ma2S+SC+i&O?SyT6-9~yiEenKA^ybM24 z2j6b-j}ZUG=sZIm2k$2TUxwG^?u_5;a_~}zmmMupAZYrypsF9cD_9fKJp26aKxhj1aSE8CR}3S_mk&`?Qq1xzXB}d zH)7#Kz|p^A2bYgcFzbBC;VX#$h8^56Y{HKQ7X521yn{N&v4eXVo9IxMyd`EFn77;_ zpJE3`EOF}tEax?1;mgT8_o;}5A3^?<33IJkuWfjI0^YME`Cu(;hux zFxz$}KX9SLAE3U24^fAc zBEcQSR>+Kmdly?Y-QW+2osaGq72!j`A$~9j7a`x?;1XhoFDLF`_)6lA22Zs3Fpigc zMJ)N5L*98tNi00ug~pdBy4OFi$41$i8HbAGcBH2_!}*rSopQ%opCa; z@I8T}nOMGiYKtmVHH}tI@IhjSzTg@5G|d{1iLH!ZQxp-HaV#;WL1v zS%z;9Ecr|`h#SXT6G?Y3vXSKL|J+H2TEChk>I#j6U-%be6%a zZ_%Cxj|MJuc);&IN1tumQ98kcGA|7Zk>qg-P z246+q!Luzs6*&A`qeCoVbtKQpr{IW%XZs3g8lG5q-FCVd-fla+p(EG4#G=oz{KPk+D2@V9~pgO(SHIseAMv7!tWp-G4Ui8em}6} z8?o>&07uG=9b(~M2A1>^3;!iB)64orOrH2#{th;Fh(({{G3k?uh39Zh%AHtv4!53g<;n{4morxTXg=dopb3t`DV&SI( z(@s0^#N>&u0nUzL=U~yF@95Kqm^|@y*kxA|87%rYfRcC;3x6|kq{`?M3x7MX=o1US z8aR6xc5uYP-wRBA`Vf;R-iTdfIFZ4ke?KTmFEM$hcPsU)jXtsHKMXAKAr^icaP~#m z!4V7p1aR1yn1WdNgXE862S+SC8;ARjAF%KozC=bCePZD`2Zr1WA{M?6^+y_gV&OT@ zn~blBh3DKAa_%M;{zl-)C}W3Mc+NG$d|=;6OrH1&>i?V3Cl-CqMI-i!h2KT}(MF$G zc+Q<6&#Z}s=R9MPi;X_9@SMv=#=6A9rvgXD7=2>lbAiJz8=hGBG2}lnJhAYL$)AY; z;fRI5lYElliG}A}95PlR7JegetO=pIG#BsXrJIfFl+@pZXJwKC$qe|3>Nq zvGAOiM(P8x@SHD3>I1RxoFhfzNlYHeP`iL5KQ`eKi$3RAk$fN){srn^Vf2ZG=Nw34 zpIGd3KAFgsMxR*pIY*N8C&a>YzL>~WMxR*t5FSQ}KC$p!fFoBMePZFufx|sa{U;Wl z^Roy~Ec|uk&ocVN!fz(u)9}Q?bDk2353%qEfFl!4e29fV3LHM$*dZ4FII!3u7XAcq zWRjB)7N3F6ESzKP5Q}~f^5+Z*KwiQ2CoG!oMZ6)kFTQ(sM*5LZ!r|_o+bEqnq!>rKthUckTj%7C;U1<0Qi@%jT2X}D4z)#`78~hCM zjRx<=Pv|Ct50iJoeV6>rhCf04GlRb-t}~do9zu>jFVzI|@<8&fH*j>3(J!-fF0k+r z3s(XcI{7?;{9<$LI7{a$3(uy0z0s*9?}WA3(pg3wW(?fzVKM&~_?{|osQhCfc;vH2zOEr!p)$tubMgbNYh zX7E+Sw;Q~g_~!=S4=izb%EEhzR~nta089CvAYNs7&KVh9ZE!~mcLOeT`0l`=CZkh8 zojVLJB3@(gFycE6UPgSE!M_JCTx;-N;=2vzTx(HQOt_;K{*XEj|1ogrmqv$ksYx0Y z&Jlv!0ZY0#e_Cjr(eG~Q zmd;fco=W|Pjm|X|KcBpl57z@ne{FOch#xU{nT1zV|53xQCBMz!&BVVkc#DM}v-CN) zSjai{0CgTS`bRDNo`u6WUql}_IwimouIjU3;VgW!h~L`z=6AZFT0Q*?W^8)E}#wX$&kMBpKhk^6OvD zS&-<_disJye||je&Sa42l{kmNKk?Jk8MN}}%m%GUPIAziv}QL*^z==AV0kjD%$MH$ zYFa+x_1Shl(UrLitizFIj%VA`7+9*TuHGC4mIrL`Q{C266tvdyKErRTzSD~TyY%nU zYikaJ*5Uc5Gia^ho5Y~Cg7ZajTk&vmPNp_UJl65_K30~``R%e{=_Sl+VA-Ibo~OXa z$!FYVP6D4NKbNM}eims$!;9kdzD^Vy8v>>!}djNeL2WrU2XaUVtzWoH&W!)gWwpYe#$WV?Kzz z#lW_`C=BqKv^cRR=_+?TUV*(LVDi)#d+UL1d;Pw{-Y=bF%JEB=aY+~CPPVD9Q?7azl??Hw3x?mG~eAI$}w;gdVZa$Y4 zC-y3R_I8Xh^_TXjFZODD_L9+{cppZb*qi9H_X6x)4<*{;^Fp!5TNwCv^@4FN}c-LZ*1UW(fGM#E#Qa}0mP9xsD!d+Ji9zJ@>T^}r_fczI#l z+YAqun>ew@wrbn^8ul12?e)Ya_73{&@%p*97{jmFd(LNX^f<45a)jt&ed($ z`^aan4)#_;kMYa3?4=_DP<6}WD%fi)etfoOr#Cbn^Ljh>dRg|)_1Rkjdu{bEIX-(I z!k+Xm`Ifzy&)!PdV}BtI-rAM>>_sl~>@hu39us`_IKOyX_Ad3=8)n%nu|;Tf1v~_O7?=MWG?_yVYlp_bBKuPV6o9+1mnp4NzkF_Q59h9`xCJ7xwmxG5m_X z%|3gaM`u4O2JNw2#NP8hds}g`YOB5N_1QatPVzC>p*_C;BKAJ?+4~mu4#8g>ytQlb z+1v7C*Z@a+W!S`CCi)Y*K6AeAA`*@hdmW+2W6B-=p)1j^IqB^OPV7Z}_Efr3b{Z}|Ea@EpJ+U|1XRi+SHnw5!BA>m_EqfPO_U8NS<)X38ZNuIypS_1Bdi@p0 zF%m!a$9Dd$hCTKh;v~ImeD>5NrQgS3dGP!t_ICK}<(`52Yhnz)V()i8dpVOmd*zlr zK6AC>_a5v`gughk_mqA zAK^1q+g`g4N`JoxZ|%h1SaT6so9weUa~i(oapG5P*{k>23wN^DBlO~Tv(Mhq>E8Kwgk|q8 zpS|6%m(xc4R{QL|Im0X8QI@^OeD>ahy(`+V_iLZM$Teo}8s^_<%O0OC+vQsxYWw{5 zqR(Cf>=nTd?O|x-mhY!Nd)2Vl4*ue#d^v8wzgu4(gS`l3+GGDqKU7_niT>S=-x%0) z9u2_PE02!Qv+cbAdzU%&1w#ebUa8OC2c7Npbv%20eD-?H^7=oVnp}Gq`|Q<)+paGo zeD+?0z4u^;>E(D^()$yiy^_qf$1hWT_I^LxYrm`yVz1F>ZvyNsfxkE@4?b(K^RIl4 zm)@(OEcPDs**gk*EPrugkI(jPdnaITC6t)niP*&6AAI&oy4dTTdFAnf&tAh^&)#Iq z-bX%r>tU}1{^BHl@A>R?nde>4Oo6h*FBRu+yZ#-5y|(Ie67=l!UIBYjpQl>(3Vin7 zfW55}Is8ie&h^==g*`c+qpNY_SM9Uc(A8eQ*s~Y&*=xVROD~#=Yj2v*-d@bUlP4Lz18PKVmPaF%!NK!RmT$LBeIJ@KQz;Mc#W-rv5bUI1zPKH;AE zJ@xpU>HF9l*@hmE6DR4N+=d>@N1R};TjLK`{HQB<74(RU90M$mG6(aAezHkfYj0en zPRuoHCpJTY@e5(YaMUeZ>P;-i^<_sQ<7kTxRU<*~l_fYYATHuW54Sn5j^kD!mSN~K z=Lb1C#h4zw?|=b)`whs+DaxTUd#p;(uV25MoRWV1`r`*~V#d#6BPx@U#1hl-cbygq z<7148>1)#4TlED>>YV#RLBpl7)Pwm4?tGM64v@ow*@1ooV(vfpp4+BQ0O?4ZWjq{Vf2Ay=A~rl~uIesValx1Xxtld2XC zOWu{dBWc*8s-&t_sbVJ>6ST8AbLlbF(&7CtGne*Hig$ls(eB7sld2X~CRHwqUEchD zT1&^1pFdg^Kl7x@Lf;&S1>N`zZyr4DrmRI(Ej`~q71(#FdakM^a{5&LVOTl5M+Giz zK7pO{8O=-tF#XFpCclmRJJLm4s-<7&|YeOJ*pBfyLV+tD1eXc1@ zb;t{yRIgX%C7o3DM=Dfa7{B|bsJsAwYt<(G+o1ANDBqzr;Ww~%v&swZQ62WaP%$_- zY`0k4*qB?ft$cNb>u$}?FKAp`H@xbT?WWGFdhg`%Oidq#Mq|mUZL5$*w%qnC_O{!avmaN=G@Ts5kCd z^D)!1s;XuAsS^zC!6RAd{R4Q#A*reKX z^RnH0wydg(cR!uFzaqG&zN!Jo1XVocR4VPl=WP09?~zpH-JgPX)1X$>G&R0IFX+Z4 zxW~~CL4P;xGzITa!QiG&C%+9;Hn=T^$Fjv-`}neo!P||F*lw(9>2~t-OJgEGy=7U& z#jlH$hueymBfgh5=heNU>Z`gSuQMBasZDh!@O6M_N!(7h5x17^r;q=$xG~l(tJ6LR z#8T8vT{HW3RoRcTcI6*p%}PC7{W5D;K|@pexyXaA>R=Qc$DVHsYO6 zC$mPy@2N`4t~_YfsH8oC2cdO$V@A_m8A)st>_v9gxBTtcmn~nuUyEaK`VQo_T5fWMr zYSOFC{&ceCA+5*nLffg|o2mjx!=kFp)Z?8xZ+WU=a9Vu~t!Kp^)tI&`4Y_jc$hRGy zUbg%0^$ho*u8n!9jjWs5m8_Q%S|Dc9X4lG=_QAIUI1X+0?9*R|=BcMM zVtWR6&fm4Q;o?~C?$o{2J0z5XhT%JHZAqhP(1Y@1!mMpZu%#;+>qnx9c>iwhkj@fn4H1%2eU1kZ_=`= z^yfskNBP0K@;0Zz^1-|IC-BeQop*n_3Z}=JV{c{NS)mrWJyw_UMO8PA%RGI$Q_Iip z`aJWlD%J0+sKw>EVC=o~i&TN{d>OR5+-969<8>tbWoraF|> zq7!P<_PmWL>cQ=jSL=rU>RHyS)V&1_9X?8T^P*r$ z=jR$*#oQYO6f~eO-qBhkw^2e2##oy}SzTGFiI*{`Z~G3JhpPMVhNQ}GDd z#!B+p&%M>$v?ME~rNgUVH!VFcWl?2I;N-XcFkV?WFZD3bC*D})s=c#t*6pjdE^mCk zd~ny@m%FiV>fV^t^u~(8NyDYqWS*fqJbqFAzJV$}>kH){m2A`FlKQlUxBQ@QY5rZO+@0qQ0=o>(M{Tak*o+jkd>D+b&9q z#jNXz*7-H>Imxez!E64z{Q73+f0tiNPpJ3{%fjtZ;yF%+#3^KyXx@dSEe4i=H=N3<7WhOnMVc78(%`5+HcjVtj45w zmJZsxsDf*PHZ8|EGedQF?xMaU?iq3Z$h$|@SD|jbb0YbP^8AWBD^iDlcKPH9hb9d9 z@wOl5Ua|U$c#D!RJHW@>cpz5_nVu3dW*XI*83b9BvH6yneGWWnN0ti4&b_ z9?uL0z3b-{HLsdfqwQhTp83-=QnM>B{rRROlUlY!)UHlenlUnE-gHUY)c6B&1#eWeN5#og^YaL#9v$|=B=ea&e+TU;VKy^m$WhCc4yj4B-c;3Sq zYE`q>o3<=UJ1Vs>zv7AVwrfICY?9kfy<^wB6u?!?ZMepMAbD_SuW$9v_qf|05bN8@ zlVaYP{kf_^=p#K_@q5$4Sr{990hX=&kw^H$a5zS7kA>b!!6&au?PrY{wHJT`Cr z1M0=9Nw~hlxVRGI7jo*fn*&Y3VzkcVgXNe&Rk6OV1`7+pp^ea7C`= zye!@|D8u~F1MobedP#nx#A)kNuXi_hMBb@tS(=XPKw~u_M8qM%Dq=9Jd5_M^Yv(aX zn6lhGsr&Mut=@U??L%*FxMg?+T5SRSIM!GC9Sq#hem271!j8&bScP+oXL>RGPey7IiP z8&dTZ*{x|k3SB-gv>~O~lQK7?1S>LwJKPwrjWlI-QBU2KckcsVX9gc}`ONeNwJwsH zKIrbe><8hq()G#fL3zC=pG~>Wl{3>*lAI&E(`T;hlXupGUpIX*93!Fh4CI>ILV_vl zBAMA;6vFDsBSyPEO*$4!(lHvnn?6OZPu@Dp<(~W>l#jY$4TjCsltFjob>9sBWtT@j zowfOE=F>;dq6K)zM|gaWC!hJ?*Xzmyd-~%{SLK#q!QrOhr-8(l^=UwA(r@}6S0&@n zwt{z*f32F@ZAcC5$ybB+H>I5u9JKfM?FQ{`N_!_bXxE#eK|3EM=BZsSws1r@)j$QXX=@~IPs&G%ZKh_uh?Qu6oAcf^(5ihS-4UzU%6=y)cCwjm_VIb)ptNwt zF%NBa((7`S5l;24V?yO}1-z*0n@jce%*{u>yB+0jh=$ zy&$Le^`j@nf09!*>e8Gs!!H?HI4&nPYjVvLtVxyw|7cEWN#W!fwddyy=u^^1?!x0( z$9Z?Jd5`_me+bXtK*HT@$_aPqA1Qafl zEEh<4^NHbGH~OI$&`$)s0|NTZMO*IT30fZymn;{+6TNdBc`kmF^%Fi%A)w#nvt_=C z=edA>lg^fNJOL=6-(>Tsfa>K5+CSeX_x8yJJ~`@>`>6g2OdmDaky)-u`b{fOA)tQ1 z*9tB70}C_0MGB8BELWuP+Jg0dVXBt5x*Dl^zIHEGsd}DvzOQ3ks`@Fw%_5@w{~dUk zCI1ZgUQ4bA#$zAHrRsTWelH@9zYTbhC9eXmb1QMGo+E#VCG)-BF_yd@@?=ZC5AxU8 zj5FyIiBGtg{sxz5r=RWKZ^^#~UT4XV0pDrKobvrfOQt=qmS<@DUaQPd&%ocS1sUpD z$mQ6K%h2z}T__?fy$GCR$+TbT%I&p%FMdYe>+_Ge`O#j#+jgDhe+1YIuf2K`@)FDc z9pD=+`2*l5ESXcyPPF9Xz;^g2Ay2mazXJC1yS?J$HNMkqTzi!YyhB9v4`)F3%DX-0 zhg9k_OTQ~{z9n}DE^@oE_UdfNPg?%HfahEC`M`Xy*SPljTkAJ1xeWMWOCAW!cVI<- z5OB364+Xx=l7~^Y;xh`e_dZN}bqQp?6E606ENzO+sUxZ zS`Sbi^*gBfZvE+~GJv^>KBsNtyT2lL19qlk0A7jrKgJ;13Aa?XWR7ZrwQMSPd6Q(d{^JdUi^%_(&xXN?237ZLV27t{#1zI0QZ_rN=!cT=4Eprhq~JLC*Y zz60_Q%ib>_^Sw~xx~cV${pk~5zCp$E^zzHd-gqFQ?#JAUh8(iw!;ro6b9eP7)%7Y z3w>MW`{N@l|Gz?>Y{@4ee{9K|)4}V{dT>ytDlGq0$X@+0;d}AvspIpZtKU<#hrTU` zA$#%ZsUncQ`1DjgA-`=M-wU!AzOi5K`se8I*k+8&Q3cSqyyx5XALcYeq#TdtIMmqsAHtP1NvV3%2j(Id+pKayXv@pp*`;^%*YqI3Pv97 zlWQ&c1%~Iw5ASd=JWGBJa;hc24Y|QO{yoUuEPu{X@>k2B@pH2SJq_iauD_87_+&5r z#(%2MpM9ip`FdV7_7NhVLiir;6MKGRL4I za~_pZmVaV-_SWh1`m5e52m0OQa9l|w>KFL*jU0CMjqKGABj@`33w&~!Pafoxz4~PA zdE+G`U*XfA;*-7enbB|O`bTwoy!aY9-{;@QCwuWT`rdfM$n$;rUVml$SNr^*u;hH? z|6`UMg>2g|fjrsr9{}0wkE3c3m0d81E$#E~nsy$rR}=l`Z7bKDii^%Uos6UX=_r;2384ZNc#j{bQ*xy&aIcjN|b zzoVMs$WP(eFh(MGI5NvTjK8NH`C%=0Q7<|&L_Kcd`^j`XXNJ-8Kk@m0Br>gC8q`uHB|0!QX`au_}5I7j9zl|xO|Hw(#(|=6Og1K{MFR1Z+i(I{8prRb?%CMDi*AH8NYy(O3 z#jcpz8c?~olshUGYpw*SRO|AC0pL(zh>t`%<~s zm@cNpep-of={tZTkM74~`cbnVx&Bn?Pp();r@tok!QAyXQDle%^iCg5vLDSBm*^u) zbdvh(;7y844AwgOz+#=S;u0Dy*6AxQ)yJ0VV@q{@lg z&d?H_J0-PmE+OUkshzKmf> zU+t^QsYDlKNq=oe7f^{Vp_2YuQ|D)iiTMELTZyg~B?C1csE-<`Ef3J~)G001F)uA< zK9rVFr&On+RHw95r=nDsLaDAbr8@qlI-aFE6{R{>r8=Ia{j>#LJ4M|e^eXbFWnAMyXC$=>XV2R=|HBvtcd$k zlcIsFEv35DN(WN2OgF7ET~cK_U1i0FXFinaRF~;eDAVOtrqftvnwL&RnXWlyPOH;L z=~R~?MVx#laXOx1Lx*2IB8JJUYGc>eVmhOmDeNpv?=p1O6m}q-&C8j>MJ8l&IwJpU zMefusj(@^bMBwacb0C^Yv7}e?O>D%7>1nta=GYmtY8>qmB}0)2lA18{?TlG4muVX_ zXJ(C?z8Xx}qMYPW-Ii;`>}i}IX3mru@xfEBi(OZ%rVW`{Gi=U+T85x% zXU$n~^{fTcuAVe==A8JX+8T$cnH8VA(DUVNQ#!XM&5qa1lzDX|{P{IFkg*u5y`7wO zVu4U==GS6^sNu7x&Y=x!k}*iFX96=(LIh^bbt5xp{=%!LVotCG!i_R>d;Xkw&4L9e z=2qc3UREw3J~I{|V=h@ZJC0Jsxn{cG7~M82tscbLJWmhgdZc8H-FzjE|IN+ib;*`89Egn6Laz zrtmnn5HnSmo?i?Fv(OY-TRvlI=ILpha1bZH8aZu#o3+o^xOBZ`?OecWzyRGGmTE(? z&3T-V3D%5pPVJJGJJhXZNa&0?v&B5Kjdf3E)6&({e3hiDB&I}yUGb$hzE`4;j+`Uw zSnJuHoPwYZj$~A+f=omDMZsrG&8=NWxs;cLk|7uCeV zZw1c61UDFp>U*H%i8l3JnvtH9iCYD>&QDdw}^$$!11ATwHdu0S7E-vCFIXD_yYVySvYWGutgmm zUh9hfV&IU&uOL1jJGj-@M1LJ{VF6jC)?*X>`-%SrJGiH?iOpTWp(t7NeW~bw2sv7) zY0S&-*}nid zJnG!&{RQMg=fOYATG|M)*z5=#ac*=G3!eiVE;ex>7M@utc8G;%R3ZbhgCiC`6*$C7 z1lI*y^j|g9a}|XyG??)htn1qk4L<}wg_cgZ)WnDSV!skND+fC`V&SWSBZJ7A`y|4z zgd8q2c8G;%{mf!v!VwF<9yszN?BLd86Mipn7QRuXBNqMuaHJeNIAY;H0}l5!;Svka zd)8uySoj{mk-<=eBNl!taJV0KaKys1Zio9Do>=(%$vbtDSa?34l=u(}e~7%(7ZD48 zl)MuzvGA$Mo_%8ByOVd~LoECdOP^Ty?nqB)h)$M%ZW0}8FsV?5!Q(7E0XXXLS6G<$ zL!vS4;BLTH=-9a#IK&D9$2J(P(oj7@Jj~$7@KZS4;3x4Da&(>{&k6;15L@UX4b@TN z5eBng68t4_)ZtT+SJ9D1htJEzW;gPV{d|irAs%J)F9a_9H-pQGM;pxNXrYS@W?vT_ zV=$i|hb}SrD&nyQPqB2C5IZmT+)f>4D%=ymlJ=*F#~c0y>Re{<8{{#c65ZRt67I+3 zoj4pPX5ql)qI{!2Hn;#d#Daq3ed^Ga244Xz;r0Y2ZJAi=MlNvVDx*&^7Jdq_^ex1~Zvz%P#KJ!T9GQq69I@~p085)97XE8sY2(DgE96yV z5(qkA;q^T>r>`IueloDML1N*rBk!~|V&PYlckB=gzmdGt#)*Z0nY`0i5DWhvu*9EO z_z!_2lTGxAg+FEK6AS+(_2WjLSolGx0}?K=@O(Zf=^_^X21}n<_zPh=!~%q46AIO6 zsPC&6PBoa-Jvzr;4pBAFZ{vr9hFd>hAM|TPCR=NFEacD z>Nq^#dk8Hy{9@`jJQ*2-5wp&uVN1alsmBhESn5&^aM&5+5et6>d1w4UEIhMb>M*hJ zOp=64OrGH~ijfBF;D|+kGq8;Ph=p$t9BRalzu2MV&Qe1mxF-QadvP%pYh}){a{{ct>tA zI>cg=>6Jc^SojiPu|q6;Kj7?Jv4bNPp8bH>Ar_uZQFvnE8Qm~X$Z*8MM}VWZku}!> zq1z2+8bd!fn5W_BN`q$-uQIp}KZUCeW*rba>wrU!&4;MdWOSH6Svek-IOx2(!|--q zF%FU^#G=o9i>xvFt@6+r)B5u;a;MSnXW1DIEcJ(2?DJGDJhAZ10*Mc?@QixoF6`il zg=ap|bC9M=bmWFd=;fvG6^i6a6K%%^Jv2CK|2?o8U^|&^=_${9)01 z4W5pl=zkd8K)l}I&G-pzFn9}oq8kn7cq6pQ;P$|URD|n6{yu{_wk*8g;L*ez4CY?a zODyqc{fs<7Cb2JN8 zePZE907o9g4vtuO*5UAvjD2F^Cy@W4;faOM0FG|O4z3Hf=znUc?+HZNaN#PjMSrEC z?gPV}JuG$@_0YqH??L=)gL~sA`iQ~Ihr&k9B5=gQvyYN?O)UHqI1Rxx{Me>IOcP-Swod$Vb+t-lZKy4{9A)(<0tCi8!UXQ zh4&Iag&o`}Y=ytmP=7a2_{!|8Hy@Qsk84*meR@L6i$J%4P`9YkhMVQC|FA5soKNh7hutpYg0OoAg8elu{G zgKjut;h7!MMu>%H9HcCWh3D8_cw*u2CGV64vG5#Yi+y6@KOpb4L1N+g4TJE+!t;7c z(raH^jRPOqg9ySAi~dAlDN|zMmyl02`ozNPevAzQE+1QTpN6_W7d!z}XushX6F+D0 z3Jc!}96ez8Uyy&^V0N!jN1x3#^n&4OPI&ee(H9Nh4Y=^2!QFvFFB#1Kq3~scc`6G1 z!C-cOp+g39*e5#WN8 zdClP2mVPaDUN`)8#BUgUgQd^wIEm+C>io&*EVKCAE&MR?n?~mu>O1*aj&h8?W%z2~ z!nX|`P3+_$@7si&GNrH7C1R-qDZr6GlSv#8bcZbGWn$6q2^=|!9UQUny@2JsOe{Rl zgHk7ng&#!TsguOQX8=dv!458rE%dI2diS02Z!tPdi`eN1%=nWdCQnS8k@v8JBNqKK z%Fg{6V&N|YmT-xMF9*(kA3L~mY{HKQX1HniB_>Zi7F+fQ*ufEt{$;?@_KAg`PM(z) zj#&6wU_R$U(dxNB$rE3XEpiMyIAYOX1}uFkvGD6Ho>=%NES^|+HZ!q9EIf-xcw*td zCSPUZPb@s!vgi{F&u&_HV&PdILLXuW$9fq0NJD*X8~uyHb@++?m%;0ZdD4U1g)Q`n zhWfqpkfUD#`-KkAypwuD%y3yxST!Sm#SV^G;=?*G^}(+Hx!@yz!w!yE^mY9ojsW58 z`ab|N!)=FOV(JsKjzx}R2S+S+DuLyh4zcjnz>!a}gCiFHDqyM4#KKPn7CXekUjv-| z8Fp~Q!m}<({j}><1NojNUBsflk-T#aPAvSBmOioYdo7+=_*X2RSon7>o>+KhnZ$=! z_-`$qSa_amM4wprUKUR*d_RjP7Jih)6AM4x;)#X7-r|XcUuN;d!f&*AV&R{%cw*sS zv3O$P|7!8X!hdV=#KLz>_VR~V_UcItzDf0)k^B%g^zpZ5U6JR!po3x6GX zr{5zMp7#l*-yZN#Sy9)NJ8pBucA_zQy%5If<1 z4qW)9;kn*K;a3K8FZ~-aP;12}7>$sc0jc@B+ygB_flPl>x>3$x-o zSoFh?qik@FlY2W9MTg;L|HCM>ii2|wl{oMm%5bxJQ^dhyhhb&qYC7SZ&bVbebNUjC zKFcmE2Rk@o;d=sSb3h75EIdtTali;iEPOt2go`i15er`coW(&X9I^0yfFmh}CluuJ*7@k=8 zanx^Tcw*tNpuSUQh=rdB97#9&#KKPj&MGoIvGCJ@BfLofM=bno;H(_O6AM2dIKq#{ z;fRHA2Q2lCSorq9k@iNPSokoo+)E@DUe^=UL)J~HC&a89kq*X=T~AmCq@MWeL!_h8 z_tyuBr(GXPU?;ni(I=Mp^aBo$F+8#Gx<0UC!nLXo>{Q_F`Y;xr153Lj7XCWmY~DP8BNqNfUwug})Ox z(#6;z7XArKpIG>(fU~Pmv(85&22TVo%r^K2^4$&I z2pl@o;BD05OQNLuvUb0ULSPcz~pO&eD5+*!S{Nm^`dEOUGsXT zygAvXox~Sj+f-@wT5OwIRz&{E8?yhCL*-@J#A6cPn@#j$#wNTr>-TQ`HHf#gb7J0m z@6aaN@a2B%EcYpRZ`S^EJNnIIpEbTd+V=almaR6Fx5Q2w)rFBMuB-gD#u3mAp)L=-x6%(x$!(V3Y(pdn<4WWL!8(f z4#KuKtgo5Rg!Y&RVs8$xZ7&rDwty2S_IR$a?XB-`*43fCc8(w8_e*THy@z3-0-QLp z$3ES**MLFM4sf)W;rP)W9~s#8K7)a)#0dNZZgY+)$1h#QK*SG)NPDy+>3sv(wl_M6 z_tV4(`~>nAq8-29Kg9RsAZV|n<43zRX4`uOgIk_|#ECr);ca^jgT4Fo z+mX3fi81_&Jzlrk_NEUp-?uV;VQloHy*WO6AH&|m;KhkO-Uqeq^%#ot9XQ(Sf=%qL z@Y(xCx;-y|7r#cIy@!f^x69uh0p6NaT34HK6@`$n(zH-FM>_tcfe=w ziZjf3$!~9u&tAJ>UVH0q+55z2?_St*hP&FN#P36&z3H&`2sp;Chh?uL4gl@;LkCev z&afiE9@p2j%OiqLgZHFqucu|Nug~6x9hACVA_YBxd=_EbyY(X6yL8$|j%APcneF&p zhsrqu{^G>mc%Qwa=$LjuiSf(DCgrinXKxqnF#yMjy&HY@*8iK=U-g2n*n7ZduVWZv zF{DGB*xTr{_xZ(29fc9bFCUxO+vl^_v#ZHpzr9^Pd#{Z%@3qoiZ_C~>pS=rVk7Gx1 z@Ye2KpS>%_quzs~y#j0!zci%HE{`W*Z@)32_hK&@dOW7w@q6KNd?W7Ii-Hq-InZkd zS+eYAG)Aj9Tzr8~uMT>&Ax`j!@2Ph+^cYT&V}SPTb1VI1lQdEFG3bp(nmKOgEggo7 zD%5Jdp%f(YT_((Nv_;n$o1~R;0|xlhEkg8uP?VG4{@eA2`s15QeZ+si-q43vo3xqq z_Es^R5jk%p=fYezwDXIDJ9~3oZY$R_O0G{+GQU4&NDZVOybEzZR)@NQRfy``Sx5_Z zW9C&?%B|cM`>cHH63poC&Psgwyp6ATb7L+!eE*>XFCRR3=!L@v-q;_Eee)9LE5%H| z-b}!LxxVt#6wZ^$+3SDP|EOBEYk69ve9+s07-l{VR=M+DM#>A`>=3Juou~S|;l}l~ z&hDI~s=VNEO^h=&2QGR{UGR$NY;Bm~>J%)W=i;r)XSujw#kDTpx?-V=-%Xi(*=OT( z$8Q@qWZa=kGq1(mx?g_TDf5OeJ8?A`&MN*+LjhL98G{uOs_xE}3P(C~0We8sVpOs#=07AR&b>x&zd(E_QHA9 zFWajJaa}F54o$4$oxe%!*I*{+kM=#hd{qsZyt)UlZdX&|weyPIdZKyEh&{4m# zfb-d(pRnc&*J)W*8Q6E8ivQ(YJJRDcVS5EGf1r&6`)epdq)>tXjf$lhoO)9@2=Fn@+aVwc$vb zx=+tH%-(9?-!ju43B+qow-E=_#3_2t-*Ks`H1$++>sabKgSdI&H7%|Bn&q9RTPpq8 zf3F+*#h)ivd{%y_f~#J5{jQfMZ~d9N%#l^My!fZ(Rq?EosfDpV?%FQ}4Hv()EhcT_ zeY7#Iw!l81FJ{2cuFS*g6ZQLIYK>k;N^)M>o?Z{9>q+E2!{3NC=@=I;9<5VmG$ehV zro?j1Esf85say0?V^tPYjhUcjj_Bbr%+`!e+o(%ZEGOHK{+_k?lC&;OX*+tvzuu?P z;p444laBLL!d&-@yt(e1&?m*SPlu8^n@}D=>sz97U#Q+cB*;~6M(yplyrg09qWfOt ztj;y_Bww~JkNrXNepz|?9^o?^m#e(>531ZHnBke}S>HcN{7`R~9B5oN)RQ+Z`K&x| z^-?w2Ss`cb&|u|lG5fsAP%pJY?eOaV7ojp|Oh=czX}@8369!3 zB*;FIc{6xX^?rL@!-D0L+Kz1yd6Z-4(Rm4ZbTA>0m_N)((+W1|I>TCy`ty><-}da< zq4kHWkdv&JTy4molWg-ZHYdf-+CDti!CD36o_(}pEZ$?+$mfkkV`tWO9{=igu1)j5 z$(>98|Cc)_Ox@wCZleyG)gvBWUeM5Y=Q+VuHA8|tcMe`8ZJ}U!dMw@{yeooY%H#0ALMDS(Y1_qcTRl#QipA;Z~cduxU5tZ3w?5eB5aX498kCM^Cl8=l#25 z-q^x-Mzv#lr2`Sozb_&aR9qZs zZH$fn@HFk4GwD6g4(`lzAlEOjub#Zov{|uA&P(2U2Hq&u#1`WZ#+u_0{^H83tzXuV zEckz)6%Tm4=;!5}R$SX4f0wkNb>*phb9X=bEY|6A$7Nd^J8;!HJ<7ffBY;Ld0x;)v zsq5+c%}CiBAA6(hh4V%ol+}#59#Q@wt{GMR@|YJERXqB9{q8J`rV5tFZ$FX3xG@JC zE7ttY4!^4#{wjojE>XLh(Dniko72!9{Lqa}^)V3h1sNvaWnn5KP*miX-$|jL|n>Wp^yJe`OHU&Xr&CO*mw)8%^#@n6*KhgxvExm_M-l$SVo6&d z_9(&=>EzQ`iEe3HN)oP5P9N_PtKV(fEz3K<@t%ab9js^x2LCGU>klYTa}_J?blp(9 zmf!TWgfZ#q!Fu#bo2h%(+_L;7>2XAZD~*_bMwq17H_iVquK$7Cq}uUbWn1fioa^|P zYyVV7GCy1o1dl(oeFxDY@Wb^%Mmpkq_)WHEY9`I~LgJ497VVFl-j@45>xV3K z^hfLPB{hZ&=qD7O!GM0kV9R_$;JEBg&wdNM4*T1@RHS=C^5@g#2@V2e<`Itjr zsGQfA9s3vB>ahPqH6HoqxgV-Yj?DYbNp9^o8UehSb6tY|Aa|_*qd&+q1r$&}Qnw`N z|46M#u>T{Uez`UGHoeN-xwVb`a%-&t`lo1_&#jD0QGC9|=NuyMfXri!Yp3-qL_~Xj z4*t$-EMVK|^%BNf{yd(~v5d>m$4?Lu?VbL$KKXu2{v-VPtlGE?^%`XMMIyfi`A!jG z?_J1VEyz&EAkVk_KZZQjl0SvK-jYv2e$tY^f;`=l8Q*rU++N3jm{0c7XZ*eRq5nd~ zn`!9>Azx<68IUJia$^2>)Zy_to^eL@TCI`2_!xPksN)GV?Y(EooeH*}2a zh<73J%+Zp2LcZOS^B{*Txi{n+<#2>o2-$m1*-@21c3!=OR!7wz@-XZ83n34(N&;32y=ikwl!`hyA z)-ZC^=U?uV@d(s$M*k`i@kE~S_1cG#z4l<_dwu#_ee$2FNQde?GVN+MkI}rR(2Ar_alOBYW+`$ZLK2n|<=zKKU;` z`III93i0V-$-jYYAOBm(UU~LV+aY`XVGp$j@?q=v=OMpg$$xa{O#$>Un6_@Yh*8fjqK&G zk-hvevX{R`_VTxJQn=-=6A+kiGovsfI%K^0%iN4%y4!p6cHqd--e9dx4w( zIXZv6_#3&-=YP9Te$0|Dg?%qSa@3C@+s99W?4>tHO@lnjIzF+#$W^o8pYQtT>iD@L z*2_rjuX?Ek(C_Ce^im5Ud;N>icfDab@%Vi8vxMXGRb#^OMnA_j&|4pWp-;ZdCr|Xr zUU`_~kNW)kxb?rcx&`6qSpKUZdsj`kI|q5U<$n+4>nwQ_WUqerRu4iBS^mFxVzQB^xA+NLaS^i%6mZ(nfALRO%==>Vu z%4J&K;*;xK`^NtfOYRDLy)FGSAy2pDb0B;DW0^V+a!<>@5VBVv%2X-j2FrgSWMzf- zBgkHUl&KixZk9e4pSRjenYtKqhIRaS$X3CKkj^UO0 zSdL|=%{#W?qdPgqN`d!@hPuRzDJ(JBo{pTH-#Sq3DxgR9RxZj574s;WAdb_Lc zj!cf_k?)f)^2zfYnePCG(X-!@Aoo=FIWossVU?qBJJ@x!|GH29){)s~a=nyDJEP7% zKaBA~NrIfOE_UQ{9e!^$)scA=?O*Sc8+`IF9hrSX80Wu7eEtU<`DX1Ob>C&2sO3KD zv`;^Xr-5{p*uuEK*VB>7F+P1Axu5p$r-u3bIdA#Zb1`}U-03y=Y@?4}B84B%m@j^~ z9Ez-wVtu-!zwXd;o?~WqXnHc0d>PmXzUg7kpf z5f|^!>v-rTJqB|95xqD`aSMF-x- zVlQTK7`<>uv02VWC#+bnc~h*ThZlMoqFz>|STCzm+@H}d)(fW;>r@u&G!&a9Ys|7U zX3>=*CZ^adhohGqDb`DlloaXZR*IMrB}KGm7LzH_=_=7_EYXXul<4JdWQiE9uj5>z z7e#TFd`VaYMlbfFV_u?DU82)cqBFQemrhB4ZO2&P^HwwU#F#1SDsQ+UCa_LCAz|xWn1*pJ*B$h$Z|2-rn7_#Lw6RCF-yMag;q*+ zy_4l%^g=F3BUdyqjmq>gvL=VS4hKJ4`>wTCq_3K+d;3Nvctq@UEtf*X2aD?2;gRZN8B!Yc?HxjTH! zMe34~R}Y;uGd^b0>?w0*Ay4M(Udya@GOFgMn(?nK!Z2g_kP=H$%)uHTUXb2;BhKO) zZD*;sK#s&-iqtIVF?)(Ddv=7fil((4<~eeW>vzn=axFv0#>C=x_?WV>Z0ep>vT6Yq zTk&EbD~0&9xll-JCG(l0Sv8C{WjPUZ(vtuX?_9SB2EXGl zi45w1l^8oRi9U8@*ebmReq zy+f%DNkruAP!cX8hjIcW*}y?c5fv#auoD3r0U;uK)CQ7(P_$^gwrZ)jT0ZMhv|_Cl zp=gmJwNyl{wYH#OkM^kLqE_wyJZsIfXYC=ee&?L;|G)oDX6AXH_0BtMty!~X_FUej zu~c}3>iEbUpV>nM$wbAnX2C8+y=1pE8am&xc9<_-ghAqdJUL>0ch2kM#5gkcd7OA& zv&9DE;u1nEz7Mec5R2z;$>JK!AjIOk0xKRrFBvta1hH)RyHPx`_;QCQ7SH=&icd)_ zo@G(qh{bbT30{~&-0cp4uDI{SvN!&R*s{C+YhV)}YU5)O)1JRU$74#QFoO`w&n#fY zUndsN-@Pg?vG`_(Cl-H;!xM|Y+u@1DKj`qp;-7GMV)5G@o>=?~4o@te&s`J;msmXa zyT($OLOcO8NMB5>H~s`8raf^9CVodqDZwN^1ArA1f>=E7Zz<*?vG^Il@=q-P=K&}1 zT_J^7_N;T+tNxjvWtfU5VFn?V{Y}8J(@;l0miEjxhb1+4x8fc!CR-9q+3i8t| zUQ6t+dp)sla|f{O?<9XFW{^iQiGLDU<3+HY2>;2kDFTlBH@b*bmTtg_voV7Zi|-Gt z7=Fa!#{w%RYV)14_egg!g1e3ns+2R?bBEYh+EGm zLT8)I!;+ZWB`(FJu`h^~AGRNri&*?n;Nn@BL5Rgy1IKvN0aA@gyxCv=`6ax+if7Y0 zdwU)XF=V0P-r!gpW41zNEa$|lRqX(hO&*Q)`KVA{B z_-*8CY<`Hv?;`JygG4NTKQQZ=LM;9uu*SF`7XLPItk&8Si=PNe{)xr&U5FSv00^=8 z?~*UDJh6E8tI2xH4|FiQ1MyA38WV&4Tk-vRqjNEXyo5>X@>yGSo}swc7+;Kf=x|hWt>I4(Ar!` z8z1vNvBtz`B7d>9Sw?=b#m(d!EnZ39udB6={Vl*M@15j*n>?)6=`py(`Yggi`=XTg ze%l}x-yb-^od6*g&;6x%mc-(R0?QAv_z}Q~OE7~Fiys9XuOgcr-?18cF*_AVH742D z0BgI%;#ut2cvJv{SbR_5@-PX$g~ff=3c!2(h;%v0l?ai#NyfiXzUka@%-7N_CqY5uC-r?#q%^% z+-mbfES`su`f6hFU16%RTzUg1SK4(;9L)Y9y2|owY86*o%tg}2_c{0x2R{ZJU5y!J z2PWC``CD|2q0LPDjtcX}eaA`Tt`IBF-GJkxF@q3`j{+ywl7(+f;`;!{hmp1KA&EZ` zdhs=e&Rm~RE5#HmvOKZuSy#zxEpO`TI~H>;|HQIqo{QI!v1Jj@JS%3Wn}6DMS`XJT zHQUcX=sHG-<$n-x;(E*=#NtN)>pGTL{3zf=J7y4K@e_e%Pb_{iaPbY8L5RgK0@n2y zv3Rzbxc?e5vG`_Sjr~F_o=r->_ld>d1}s0s;@1Ng--H=tJtpyY1INCGh6W)P&-STf zoLKxzq*E~S{ zW6QsQMFj~6kJE~OH_(h_Cwvf;#;Z9*{E)Tr(EgJfEjGU$9=4eGjw;w0K-fMzZL4F9 ziFvlHGEM}gF=2?se`Z_t{T$O)AGQ83bo?v;{vT_ro2>uO-Bus7_Q$kUKmTk}Dld0! z;-}W0Sp0Ti^*_YoKeMeqZvA}jw)!(`e@t8T^Lb2LeZtxw(^h|O`My{rpS0Mt)y)>0 zw)&LCrmb$V*tFHH7Mr&E3yV!#-Da_At4~|}Di#$!J_wwA#`31E{?cO8R#{;X(^j9g z*tFFh77xTiZIxJc%QmcQBx3QSffK*N3_>iv4p{vvvG^Il@3U3wU6?_LWzYQ*J2OH$>-Pc= zb(M=)_H6EnUta29Q%g#Pb_{qFxxYQSo}=j#Os(rh{ewW)^>@> zbG!AJivNfigjn|Tfc5*4So{^_{cB@l@oUJ>wD}e)Sxc7jj*2{Q<>?AhFPkCa&a>%gi9V)Cqq{g{gPVFn?V{oBC0r%EiIeXQ>B5{u`x zx9)`!iys86{Y5OE_Y5`O4zc**z_D{NgAj}F20i+Qq0P5H(KjuwA%DQ)@mM4eTD*w( zEsLA5sPOIC*J*qh(}(=o+H9rGe^|T?i>Qy;lr@G7UCYlA@~jw0H{>(S=6jV;6b z7~*-)LF>{|eKIl2nCDL#TZX#Iemrn|1ZEIo@iAbHEki7x$7$?*Y!E^$z8sispa4r^ z^27r$6~Buagjn`f!16;ZzAJF@JCr>PXEBQG#|HR^XS*Gn0i+>kb+a(r%2w3Bs5R3m9Smz*O@%*MmeG9Sp zBH&`qAY6#&c~JX{m^}9v??Gw&6Zd{piDOSJ`*Pr7-T;OW%RldX$7WmJy$9YGc1eHy zlm5U_o}eH@F^L~b?2lD)Eir#EL2e^H!Qw}ND|kYK?7)=lZt-ph?+1?ZCLF|^?|N7~ z0E-Gg54=niHs`^f)`s1R+9z?QT);Y~g!|Y;FT1YW#}20-zdaMnk2wZe@sJZS#f~>H z>)ti%LHz@<)~y3h_~UpGi=PD?tG7Oh#a~Xo95V>9c(z;lciZs&;1hi43L%#LGr(%Y z#Ns<^+ec_W-ufYyJ-arw4Px=Ubk^@XV)17=Jh6BdRrlVB#Ww@%yi6><6*%FKb3!cs zT43#KV(||GtA2>ZKLVWK!2ux_|0J;XHL>{Z1{?`D@{<{wU z4DEfJ=ZSqkIq;=xS7O#P`?LZ~3Eo8avFbkxUDq?j;(G!o{>j=Ci|08vc7f%I#Se6N zV(~nrlkEH;LoroUSo~ifGbjAviZk|zdDGf`Yt?@X{1_9w>DgftU-%4t*^<4kzDnV1 zu=*w?7&u06IcL2v?Koh1_QVOi54tWbHBwRFChG;{kY)Bu%$;mA+o+Rme#T4Bp=}=X z{&OguS~yuo>@TE(UFNt@J=!;(v)wSwZ+3VU8gGod zA7VMCY)7 z><`$E1+fSE@VZqZe}4wy`kR7#B_)3Tcs`duUZ=SJmgd`Xg#9r#o9l1U zC|uw9`HMOJcpc;Vdj>lnyDlTjA5ZtLzw&C&dkR)8Z%<6h-{g?L58#jK68W1D^0&X* zeiuN0y)nsOeaK(8LYo%$*A}vic4D_7WY-gRGr&odzk9&B-akq^0&52$Nv5^LiA%B1A#2?4Ca{VPH1^)Uw{x*gD4Mb;A%?kAe z$woGY{M`qCqx|D-04$V0##D0m$A!naF(-oh+a2<^A07Kn_!*07pyTgo$lu-YcNNkl zNH)ST*xd3yI0f${fg6j|Bqrsr5Ay8huN0lxEaSn>W}@lo4>d5!?F>6=#TxI^0z$XuLzy( z?hf|v6(N7q&qjX*j{bNJAbu1>W7X9JWl=1g8?9ioTt~a9S zyxsnk+ok-j13RY}W*^XwFc8qlr z&Yyg^{+d6h-A%A#zS-6&tY_|53DY#G>`beH-6EW@#sTy8H}ildbj(;+j|{*)VD#k@ z`l6tDWcDrf@!#;g?>hyN9WKM?&TeX2(y$o6paw1c%0C+G3LnG|9Z^F0$75YRINFxY zmUjS-k4lWAlkc@JJ;`gobngkbZf&27QFRh+ksi~p9@FFK(Zck!BCp4R!lL%2ZLql1 zSYQO0OY?Kuj~qR&_!sSO7Uq;S@A1;LMM%FVXZf#MQUw^f24mECN4yoySAIhMx$ix< zxuP~*T-Y;rdp^d%NkqyLVH z7#XTy*ORLsZLMk@v^u@(px0iQzdn83(VQOnz1F9_9~P9Y%fpCKKUv>iSi3&mkH)a#^Y6Y*R;RfC2~&Pkb>c3me)?s@Il1yTlXzbP1t%G0Vjccp@@V6^&es&YqDDHCTR*bZD(%PqJ97I=DV-fmNg&n?%CXy8CQa%53CNx z+rjlN;@x}8Q|F`}E;xdb6$-qz!hPR6@Tp^iYfdvZ50|)5J+*K>$0*3Z8S9p*4*U#d)e)AjF&$@iGY;Vi= z;^a$yV)^F79rNp-_TtQa$%}T0wrq499cCFVFiX~q&8KhgrytAv>xb71<*)4jt?fG7 zVSScqKRET9U4HE!EU^bUvV>W0z17)<$ZG@(rkg#DktbyTSvuR?ispM#>4QH!o^~AJ zqHF%h8~cvYIB}2Pys>pq>xxx-QnPE*2R7xLgYiVTCpq?pT7Butd(C)oE*Gf{MyObg zb@$zhk*vvf)Tj#R~J9Wea~ZUOx-=1y`WJ}xL*G1?$6r&lY1tPI9#L6#Z`ek zmaYFG_I9LukE;*=-HubibKul%J(hiN!jEgy`}4a$zPf7>Yl`h)Rd)y;xq zQ@HYZ#-hl!@8POA(%fHEydLKrs6cYfn@_IJJ3xWtnlCY39kF=>73jpwSR3>lF;B(= zgIw>+P8@pjU(SrZvCi0k*<0Y7^EXcfzu5gxK>gn&r+B=tN?|*Wc>TQ}7|s6PjNoeH zeqXog1HE7Py7l)@nYhI^eSr5LS@tLUnqB1LF8I+breBtpi|zTZ;rM)6D81ag%NLO4 znK;LG`)V!LBG)^~iF--zBySK6A^fQnf$mB!%I}Aw@ag_;GWPGup8t8f&xgx5z&qXd zZw>I{&5>O16fb-1iBp_Zwx5BSu_$Z;@7M&A>;04W%`E@_6eI%WV*g%}m7X+XRp6(l zFL@rH`3I%S^R9*-{9euT)~AU=+be|5`%_kmyl&9_*H%CkdEKF(rDW*6q2KN3UxfauqnAK` z$`D^?#a6=Q?YA8s@FV)e;V z{q&%|Z2Fu~dQd+$y(yG_MM!TA>1#v!4I%xukRH^p-QNA7^hZMa6Cpilk9Pf?q4XC* zda(bj{r*t;+adi>NdGvb=VkVx_17(=2kpV8`>&qBi`C0R_Q{YwG^CFT>A~?}{RRDn z)r0=T>N7*@*N61Rklqy1uL$X_A$@H~zagaG7Sity>Gy~9M?(4&A$?m&-x<(Q3{ndo@Ng;h&NS_(f z>qB~DNN;j<=H*0f9_{zEOd84KP5)DnN$+O#!6CgSq&J526(K$7FRZ_NL+QcsZ_{^$ z(u3o>o5$y6!TGM6$LDN8f8Wg;4}F@m{^`&kaP%{vmpl4&=(8OCTW=t2IY-U{dw9eX~nbJxEX zda%Ev-VM-$^Nr0_)&~I|=pM*Zb(YHaL<>))0U+(D7Ll3U6 z`gkuwKj@@qAOOdvdll)DD8Rvxeki14B_mgXkKuSb-q%TT{eB_+DO7hW$WCs#0XY{$);y%;a7En4EuUbLtIaef!18#>~= zMR+ZZui%*%>;j7{+Qbs}(hUm`Ahmw+)P^O2okJqx@tDcu#(!;UWmN_;1hgejV-c~s zvML@$LFM2TI5!+PqbJ+d7;)JmMyvMyXAmPgTaK}m{Ztc%87dRG5(7u0#xP@wnQUxm zYFy6-*EaFDO^|70Yl20yAU1rsacWd13^5M4?qCKTA8I%g&Y2suL7wSq2or|V->F>J z#OyYKxJ|rm6Rvuw2^DT;bYOyPpT^ZppluVCy3$0Y9$}K162uQ5!F-IMYa60_Fkz($ zzg=m+=F@1Mv(rrti;WhvaH9qd z)~H!6(XJgdM5A+a4DZWm)@-UC3BIJ7Mabf;*^QSpWSInv(>_yUrcGm((K63hpk1|H z`mbqd%&3lOO~c}bx$~DG7nxKT%Yom_`Iac&5XBK|0`r=eMw!(IvriIoj=u>!D2AZjFbFNR0vOO%X z6QW??vG?M{=xzW^9~_aTd2?5&Khsb^nsrfU$j+ zCl=51P_)MK?31Il7W2+hlpBXsV~VDT?7MK$@fJ_PB8vC1C^Ils_!maAfTMU1ic*g$ zI>|tDA0c|W#m!h$_!kz}11mo20}lTxF*gF?Z%V~Vt--lW>rQm=3fQRZ#46+cy_7-is?F*_-wmf?vm(qi<$N4ITn+u;7)_EEk*Hu2W1{6#Wh_= z?AzQ%d@g2?J27e97lD&>0C@$I_}77x=VJz86<5q6ve$a@`8M?EY(v{`sD1kw%BXeu z%}a&P_W{fnK)*mr>Vm6)P3o(Nb%bvR}?kmLNS=8chVg?}=-v}J9Bzx?4#Kla6EW#xFCg6A< zvUV&h@ynnWUu5X4IE`0;(sxY6vTp`1UVs^-8I$-{;P{)EL5Rg)4V+j=*7JzPuLWj) zSSQ5fiFphbGvg3q+1~(6`ywof$rIm#srX{dAjGog@k@KQO=9xI>oFBC#tgC^lkD#X z);GVz;_m}aEWr#yEdG9A`tOD%F?r$#F%`42A;hwO1eo?wEQ!ezKZdE;kB>?$`zL_o z2QY&Wi+>6@u@p1NQ<%hW1E&9;SQ3*b-j1pGQp_O4vfl|z`vjK6!l>CQt04&s1DfV%g^b z(>@6kV)DdAwEq@>k7eJ@w;u|gm^^Wm_RRngV%hif?MHwoCQr;~6UEB`d@TFEzWreE z#N>%fXukr$$Fk?|W#)e*cw+LzJYN^L0Qgw;N!owK^2FkK9#6DdK73B6|4~TNIh}X} z{kK_rV)+>bO#5mi5tApbru|9)AIrXm_HS99Sp0b4#45`Zi=RY$_^&|{F?r%Cz{OWv zdt%v7^XDy045;1w=S+rkc?TKYyPkaA-L@a(DuwtANi*KYo z@;?Bsh{+Q#0xn)_{S(W+$+sVfBx3T!%V_UEv?iAQ6~6r>*btK^Zl?XU06vy|t8YII zJTZCVt7-oo03XYKt#3aaJTZCVb+lgx;A7d};M-3DPfVWp7TRA2;A7d}=G)HzPfVV8 zJ?*at@UiUgroDgtKrH?~V8uQq7JonOQU5dHhnPI^gTRV?N-X7kzt!3k%l=i```1gv;$H_&eAn6&i{DRs$&0vCVJ+7rwE zZQs5LNyOxd-=+O+)}C1QhkW~GNFpXr`~mH6xAw%c|Jb*`97)9FiM<@y-$4KYmVF+u zuHT5o7Xd3yDY5u&v`2ZbfE!}+#8Kel^#BlJ+4uDA7l9`xPn@9r4*+~D`@X(?Gk9Y1 z#3i)96Tru^FQ>i#vI(*H0le9iKhS;-)HTK zWj~GfMV2QP&*wXdA6cGQ{7l-PWO-uovw({?Se{sXJ?*<%o>=@m;Kcv3JhAvj+QWYy zl8DI@F9I&U-`W$)zR9;|5s1kXFQfgBtv#{q2Y`w`VEFk*3|3Y>TdGYGNx5x}}HK`g!oSbm7bj|Wa{#0)|#eiE?mGZ2e!1lGBgSUiuP;)gMV z5Q|>{tiFp_JTE&Ek6;EN7QY5K_6}LwM#ZxmVjH6ni(e0{c(BCc*{o^Lx+f-2ya7|< zQOqF3vS$@2ei5XWqD%p-5j1+e3bka%pk<#djiW3vG@e} zt(ZZG#rFj+`vqD1yGVQqbon6`Urv4-W)NcW{eg8%5o;d}1Wr7S8H8B;AYdI+#NtN+ z>zE=IKN>jk3}z5w@sogcOc9Hp0-X3IW)NcW(|~nM5sN<$SZ$A3{2buKcFZ8e;!WFo z*3jmDRC0&KF)X6LvY7Q8-D&X%EGnL}cqTCq2*`z)gsJP^2(h-scB0s@#Nyc=WKS%f z?V@pq8_8)gt<@%IA9J|-KS3y3#jQa*`gzm2@VU1IUO$ou&x7SAeF{i(^22xm6~6-kDJAxOQZIXzb1NtN?Jy5&+3zh!ES|?v znV)B3@jUj+Siult@jUirPb|Kt!xM{VoyeY8d|&c^zzjkxz64nI#Nx{xo>)Bl3fU8j z=iyrRI%W`J@hp3Ul?q|GBHTEHd5#<~(BvobC5ySO$U%$gC&EHNhGLTaDB=^z+Wj8! z@9j*29{IAN&HV-0&m>=h8KfRlgpCx^h$+&~K=b=h_E(TEwR|hF|J!FRahc^gk8lS< zOc~k9343TDltD`_yQpdQ94wm_+vPkVMuYMO)thn35!uHLE!DwW9!_W=raCWtgi^BJ z{RqMnWj{%G);Z>xw@q8TkU1OFNve%x6@EDG2e<;A{fIo^^(@^!C=cbrd1f9;aUPL} zQbNze-PG(y<2FU53Z*fyQtp`slP(BEP6ufMJooymDWxu5& zF7t-U65}dZEqE1XkWI>ZBjyOewU#A*N%9Ij&PlEo~{m5S{uGLhjfrvtPWJC&Nq@*@#vf0!ZvGg#r3jk{YZ?e>|Tme}4gX z{nZx+c|}qt-@ION?Jj_w^6uI(^rdTeA?!xbz!&9@*A;I5wqvX@m6zv86#g^q`3d;*PhnsKe^W#Lx}9pD711A0&B`CYg>wBp z1ApZtd{O=uh5U_zzblYPe=LvueaH8w_1;EfN7Kp(?I>$P{vJEcGtaT;uP-L~yBBsm zUT|0#>D$HDhp!qt;-C53b?w&42up4E(a?5Z8fl+*a=W|)(RP0m@^?SB%i}^Kf4t95 zT8~Fq`FjI)ZaE%pLSw5A!PS>Cw2`XyPtJp*Br8Yq7%D2LUvEVj^&rAyg&EturRW{6T8P?z;&vv znYSJLjr*7TEs2R~)SvhLsSQ65f4ruZu>Le@n@r2YK^cwOw*M#Un4lgLyQU`&o(&590my}cu8!~*T;s0^$ zn%1c{KeoIBaKB`}ciF|0cRjGW(JOtS|L%#;7rZzSU&7yv{2gePU;`qCTc zAVx!v{2zE_dHo{dW3^K=r9G)GnTg4OhyYXOOxrze>^U3HDV@>Jl(+xx2U=N=6Q7^F ztKdcEN?urx_qMrPV?9pHcpio=%Qjq(KSvyf!v*QLHsv6K#VVk8HhH&_%ey_5{?6gN z8x3akgP$-xFa5_&c|T4a-t_xa`r5-e=|6Aso=v5{eK_akRC?v%oTpRIZa$2d4oKO* z$$KaDn@y;be5A8(jyrerc=#zAg9*#=Shg3wdqO(ra8aat{~a4zd*pjLr8{jrg|oI6 z>_YUL)f?9ga_au{+Weh}{_?jnyuWc5p23%OuZfi942|^2Z}n!^r3<^{?8ub#&bA=3 zfg2U5bmH|U#zN4xUf#@D7K~_AuxnszS;0fzT?Jb*`{GMma#K%xKS-r}9L-tgwHLfz z7^%C{MB89wB-QKt)pKeXx#3ug4>u#n^+O7dE=GUqGmtFi{KZ1_?j3daDyM(p+dCIL;Ht3nE zZNs;!PQLV9OC8%-ELB>Es7&jQ(SQIoxiV3AAGdv<+}8tJ>Bl9ORw6~HNF4Qf*b47EB17~wJv?lp04>dW9D6%NcS!1 zGBVR^4=HG?u1Yal-PpV_%|r4Lxi0_lwlT|tC~O;6owH^6wyHWtwR5+3ug{T>F5Z0D@W_StPTihO?FBfqUcDy=%j5Qa zZ_g3h9aobsKH^=2_&c{V{kVO1>^V|dz}#H3xmV4(n~$qGeKYO8RCA^2HSGRWd(VBQ z)*IB?qtMG)UUzG4aI_A5=CtQp&d&34>uS(KrXlteu6fSJHBa~V5N)k2=3nvT27P=> zs>WM`^DnQCv=0Zic7M96R+?fSvDZ4i=DE%1UcB;|JGb4tb;nbaUqiG!L~GlU>Qa-A z9w~^_{OGwst0xszJ@e>lCr_qrV_SSj1XQjM--H&{g}frU ztLq^zeds{m!>h3Vc;@8sn{P?AyqNNGUp(1s*@FmbI1qUa-|v%S^K(wE?d$AyK})JW z(hEJE?Nj{{UVpfV(Y53y_pEJ^mb1zuxo6izF6uq-Cbrmz3tz0>T3B}CHg8Ky`ojZ7 z?fFmteMr&K>hzSy@*cnGx@)%h(Rq&8+AiwV-a9|?n@t_p z@V>Qoe|+rv;To<*T(oC5+20CD|F=THYxr&BT$6wGkHJ+`%ZoMMMZMBb-|KPPTUOjs zllx*h>M<|tN~_mfmAK9*Dk{UxCzXc3ElQo%du|=t#EC(haDOTAcS7d;h#!M<@r#gG zJq0h?Uky`tp0#7f_SH3eo;`iXpw@w_A73;4+f|vX>-PK_L`uB4SMJ_?FFoUjU2RKv zD1Tq#qfgg3QN}j34tuMzc1Zq)oa@jEdo6mdMtR+^`jqD?5i@c}&YhX_QKF{htQxdA zlotCbsF4k=m4D2sZA+b%IWEtwn}q0^_|>$ntYA#!F4RF;;q1&Y#p9-bTh2MxPveoZ zd*axC<$c-(ks7~kvAtkue|2h4BIkr zb&tZq=GyQY*+24j;i`r`*mWuXx@36NqmBi6~S976PmBJNz zo zbz%F>Xj`k&FBSH@>|$MG*z4*3y9YTf^Yo|r`=cFU3e#Uaib$f9itfq}TGWQtUhlM| zW=Aegr7u5P)MHir(;k-|J)yn8%SnIuW>MR>_xzf9VU)z%#@$-8{8zbWmZOdz@+K9n zxOqcsOX?x-)?XIYba`4ea^ve$Q=1CC2-bM_%I|C{jB(v)`uL-`ew!V6CzZw!g7gu@ z#tW`HSbO~%hSdJZyM9{EIjq4`3kUbiFC23AQ7`}7!+sY>P8eq|F>cJ>+|<`26aLEn z5;0$k-ZMUNti*$3=|&Sv(B}O1U%2@`We;=y^~jI@%958AU1$2;D>vU-^Ufyz_C;rx z8aHla$$({3W~aYdlA3&G$Qxiy}LvEyv+Tq=}7M=&KvIuQEWO61xFa?&6V7& zyTbJE-o2#GyYhkLn!8i39t#wp{}XZC+zsSSx*U=jvLF4j64G(=&yidwLJH|zCxVLA*(w9cbs}_;&2=J{S^d1g*rd-2>2pGQeW*p$ zhteC&ecOO+FncK2Yk3}Sp_+FftxaAiy}ntzBuEs$iy97>F6oopjHtT6ne0| zt|l$mD_y;>BmMHs`rS+oj&SsE00*tMn>P#k0w=uzILJ>o^Zr_JlyvhJB7Lc2-vk`& z#cn39;VdWpD&VU#`te>1@P&@fG`>?|<#-bZai&!CA1rTh6h)05w4$gPlRaqVQFD$9 zT5Z%kiw%yVsEMl>)L&2UUid#gv%TIPUzx7T=n3;JUaN++chC zz2AZj%5T>Xwl~;(hZk&bu!#W}oP~y)Z|Y(h|05h_><6 zYNLij`(w>_bD3nX&iLz{A!>|X@92D&Y+&XzT4TPE zyU$7Ix70T{dI|KPK5M-G(1TvT24mK`agl1wID?Nm>+>1xR!8S|*t;B^&!1m$bbf>V zrlU`R{=TF0J%djjeJ1p-n5@*87>7L_o$nE_3`$=Be4AACTusn}?T_~^hyIL{-VD9f z(N{wMlcTSNzQfV4ht4))WxVzQ>#Iq+WB<`K)z= zqdx(Ct)p*+zT45aLl2JU@!l@z*E{JiK)=b+UxD77(IN?QP5GQ@v8y2lX-48<6FHsy7Jw8pnPp^l6Sh5_+?vS3|$b(d(cG$J11^ zZyPh|)66$DLHnBKO@)2XpG-6McV_Hoc>H!|yQ7~6J!o$;Jbn{X<)mK-J*bZv#$Pm( zKGS>$v(C{O=PcNt=NbE0CViICgLJExhSIxb^f|_UUPymGr1Kq8D|5{I4?M=DGp^ch zsc3muLl4S7$75Wzp#J8VxN7?yd&Wg`x3?a;yFJD|yUDS?ANu2t&NyfFj?QP?d=JFR z9Pdf!>{F$0gU&uqx)7JSh)lq^XA#Nbl?GOk>=%e+s47_&Se!#!Z_VO25$8*~VjdE2zoWhZ;TMtq$4W?dvvw zxc&*H?+EF74}pDf3|CU``RNxJ{Y39LJi(H%45cA`l&|xcig_j8*L~gYpHgp5$o`U$ zevPl&?U#E$^!4+Mzy981zFuVX0p5-*eW3UIEIsM{HA^4lbuGY(5SyRDxF=wB&RL$J zA$_8++wxR-vwi(|*v4?B|8F7t+kJhcNgwV#?Cb2)WB7geOJBG7AL+f~>*Gy2uGf4W zs>#pae7&nlAMG8FyCW261eGH$wWwzTRrqpXjxP(r*jtKlb&N#(t9bt5Es@Utem{CwqSjrJsOzizxds#qj0S zDZWmQ<@;(#pB~aL4(V6=`U}S2RPTCU=Q%!xD}(hRd%p8UVYXtpvj0t%KHWR$>whun zGrW&O>vw0vgsjID!|(k8zD|z$|L2hYwUBTf;e>tSTL{qFZ z6;V0dl*m*?4IXh46=c0@HaAFNDmxY#2`sKF%-Y~~mU+N^K}q6IeCRVFOBV4*QGtv_oG%lNBs z^|6;tyKGT|u?|$Oq7ag4gp$;Nq?*6`<2TY7y3u& z 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⪙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

S&%_}v+Wu*4J{k;WhqG3*TemUl}|IKk;81 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�)#QUj#7{JD2|B$Uuo5W)OL*Nh#6GSZf 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 z U*q+rjbq%^U%f?!+CMVjC3ApDqF2VHNLU*yME*EZC3h##S9@aZR3w_U5EKHQHDHipC%s~ITm_?b)n zbrot$qtA;*sakG&ZyN^@!z54mvXk!}_l@|*mG|`{Sy$0;iPT@Js;sH2_tOpYE6LOV zHyAgeq#fo9oA~jkmeni>VgqGee?O>Qh7UL`(#_6SDDl^B*RgJD;#1`KRM#}(qf#?# zgB2C~B6>r(*&G#)c5><0*fgCuX)X)H3SLVH=hDeUhg=qMMT-`dm4%WO7pVnf7A|9flP70|*Zrl?TIF!xSOaj|MNdn4rI{P>!Y=cm-X6$Nb^ zE1b`*kNvuF{=C2=XaiKI$Egbz)!80SE8t#;?&uVI6BQ5h1p_@)_33pN2ivj8*`bmO zu@w4yaQM%16y{@LzA48j)*-42KK!m+5x+o(7LEmdQ6E?^E7*U4_=UM*ep0TOE^)@j zsU4?FoVF6$O7wT^G^KM1T}tRuLeUXi?QrUipx+4kjUXLG=V66(#4O-6&^oM;iG~%@ zZCIgpK^q{}v8E)$w8eZH4?7uRb*_gE(`kq4wCJ8-$1cjHPFrj|HP%HKHeBlr*Y@E$ z)o|@UTYbM`+7P9XmoRkJ7OtnekyGwa-Xa#;}oEO_y-kD2+#HWmDWy)F~>Y zy{M4>MY>!?x}-(AvQ0}YifbRe4T^Ntn;oD?=e$U7haz2~BE8v)bS)K`tyj!k73qyw zRKz?N6*136W>f2JU1YYsX$ELnjAbc`u)APs+K8^bB3+Y3x>k#H4HxM;FVgj2q_;(p z-Y!M@1BOL~dX4%^g+;n;6zL{WRHBvj#|w+%+zLf;?Wt?NsKj8M&*9p0q|TMzMnxss zu4}buxURPnt)T0?XrzwSTc=3ZbCIcMUGqhww7=dKMS5!%>k<{~5*6#R6zg&o>#`K< z+AG#IS!|lW*%Ne~7wc_NtXqGv&VR8kOR+9pu`X$`E_JbPSH&Y(tHrwQ7wcA3tlL%b z2|-nHUdKd!fOTyOEX zR*vhn$8}4H>vF|)JBjPH$92nz>sA!krHFfbI$LR6mpZPy-MG$qT<1J)c7NSp#!aW7 z+iG0zJ8|=IO5LL3x<$oxi;C+O71u2)uIoRp>p!k*KCZVyT(_vW-U@NuqT;$m#q~ZC z*X=5<_msHqAL6>Ni0i#6uJ@*RA=^w`cPeq+sTAv8rFfL7Pqvd{-71Q83n|vEqFA?( zV!cNfn-(&Xtt+nETZze)Zewx1@5jv+F+SSAMAuJ=-fD5JQ=+$Ki7B1dkLz{Cbxz_s zS0y^1C3;Jh=$x165|!w!R-(&WqPJ{`u7MK0)k<`ol$iP%$-X5%Qg1Ka^ThQY5!Y*v z>wYP&TU1=S5=)p4a0kW zCvTb~@i_=9_0 z#JRO|6LS}+&OW`$OpKpb7gW{!hGoJN8LT0D15Vo}|a+Oqlc=G6ktpVw%uW&Yd+HHk%bYHw7pPe8aD zkMmRsXA@$UTBPqE3cbJZ`78p9Xg3(J$to=LEfzS4Rb3O&r8_u z;S(6EE;uM$FbAg!7}m|k;2YbRS(S}0H4j5bG(jalKR#=nYSr31UsI$BhsQhygeB@q_vHWrm?Y%=5ti+AFOo(5D7j6@^*gJHUPMa}z+ALg

K3FGz;&{`HVBhmz!voZCuT~ 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

WQ`sqxGwv6@~GhkN385{&Q_x_hoATnYOL_Mzgln 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 AVgLXD literal 0 HcmV?d00001 diff --git a/CH5xx_ble_firmware_library/Ld/Link.ld b/CH5xx_ble_firmware_library/Ld/Link.ld new file mode 100644 index 0000000..a61dd5c --- /dev/null +++ b/CH5xx_ble_firmware_library/Ld/Link.ld @@ -0,0 +1,178 @@ +ENTRY( _start ) + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 448K + RAM (xrw) : ORIGIN = 0x20003800, LENGTH = 32K +} + + +SECTIONS +{ + .init : + { + _sinit = .; + . = ALIGN(4); + KEEP(*(SORT_NONE(.init))) + . = ALIGN(4); + _einit = .; + } >FLASH AT>FLASH + + /* .vector : + { + *(.vector); + } >FLASH AT>FLASH */ + + .highcodelalign : + { + . = ALIGN(4); + PROVIDE(_highcode_lma = .); + } >FLASH AT>FLASH + + .highcode : + { + . = ALIGN(4); + PROVIDE(_highcode_vma_start = .); + *(.vector); + KEEP(*(SORT_NONE(.vector_handler))) + *(.highcode); + *(.highcode.*); + . = ALIGN(4); + PROVIDE(_highcode_vma_end = .); + } >RAM AT>FLASH + + .text : + { + . = ALIGN(4); + KEEP(*(SORT_NONE(.handle_reset))) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata*) + *(.sdata2.*) + *(.glue_7) + *(.glue_7t) + *(.gnu.linkonce.t.*) + . = ALIGN(4); + } >FLASH AT>FLASH + + .fini : + { + KEEP(*(SORT_NONE(.fini))) + . = ALIGN(4); + } >FLASH AT>FLASH + + PROVIDE( _etext = . ); + PROVIDE( _eitcm = . ); + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH AT>FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH AT>FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH AT>FLASH + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >FLASH AT>FLASH + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >FLASH AT>FLASH + + .dalign : + { + . = ORIGIN(RAM) + MAX(0x800 , SIZEOF(.highcode)); + } >RAM AT>FLASH + + .dlalign : + { + . = ALIGN(4); + PROVIDE(_data_lma = .); + } >FLASH AT>FLASH + + .data : + { + . = ALIGN(4); + PROVIDE(_data_vma = .); + *(.gnu.linkonce.r.*) + *(.data .data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + *(.sdata .sdata.*) + *(.gnu.linkonce.s.*) + . = ALIGN(8); + *(.srodata.cst16) + *(.srodata.cst8) + *(.srodata.cst4) + *(.srodata.cst2) + *(.srodata .srodata.*) + . = ALIGN(4); + PROVIDE( _edata = .); + } >RAM AT>FLASH + + .bss : + { + . = ALIGN(4); + PROVIDE( _sbss = .); + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss*) + *(.gnu.linkonce.b.*) + *(COMMON*) + . = ALIGN(4); + PROVIDE( _ebss = .); + } >RAM AT>FLASH + + PROVIDE( _end = _ebss); + PROVIDE( end = . ); + + .stack ORIGIN(RAM)+LENGTH(RAM) : + { + . = ALIGN(4); + PROVIDE(_eusrstack = . ); + } >RAM +} + + + diff --git a/CH5xx_ble_firmware_library/RVMSIS/core_riscv.c b/CH5xx_ble_firmware_library/RVMSIS/core_riscv.c new file mode 100644 index 0000000..ac8238f --- /dev/null +++ b/CH5xx_ble_firmware_library/RVMSIS/core_riscv.c @@ -0,0 +1,306 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : core_riscv.c + * Author : WCH + * Version : V1.1.0 + * Date : 2021/06/06 + * Description : RISC-V Core Peripheral Access Layer Source File + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ +#include + +/* define compiler specific symbols */ +#if defined ( __CC_ARM ) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + +#elif defined ( __ICCARM__ ) + #define __ASM __asm /*!< asm keyword for IAR Compiler */ + #define __INLINE inline /*!< inline keyword for IAR Compiler. Only avaiable in High optimization mode! */ + +#elif defined ( __GNUC__ ) + #define __ASM __asm /*!< asm keyword for GNU Compiler */ + #define __INLINE inline /*!< inline keyword for GNU Compiler */ + +#elif defined ( __TASKING__ ) + #define __ASM __asm /*!< asm keyword for TASKING Compiler */ + #define __INLINE inline /*!< inline keyword for TASKING Compiler */ + +#endif + + + +/********************************************************************* + * @fn __get_MSTATUS + * + * @brief Return the Machine Status Register + * + * @return mstatus value + */ +uint32_t __get_MSTATUS(void) +{ + uint32_t result; + + __ASM volatile ( "csrr %0," "mstatus" : "=r" (result) ); + return (result); +} + +/********************************************************************* + * @fn __set_MSTATUS + * + * @brief Set the Machine Status Register + * + * @param value - set mstatus value + * + * @return none + */ +void __set_MSTATUS(uint32_t value) +{ + __ASM volatile ("csrw mstatus, %0" : : "r" (value) ); +} + +/********************************************************************* + * @fn __get_MISA + * + * @brief Return the Machine ISA Register + * + * @return misa value + */ +uint32_t __get_MISA(void) +{ + uint32_t result; + + __ASM volatile ( "csrr %0," "misa" : "=r" (result) ); + return (result); +} + +/********************************************************************* + * @fn __set_MISA + * + * @brief Set the Machine ISA Register + * + * @param value - set misa value + * + * @return none + */ +void __set_MISA(uint32_t value) +{ + __ASM volatile ("csrw misa, %0" : : "r" (value) ); +} + +/********************************************************************* + * @fn __get_MTVEC + * + * @brief Return the Machine Trap-Vector Base-Address Register + * + * @return mtvec value + */ +uint32_t __get_MTVEC(void) +{ + uint32_t result; + + __ASM volatile ( "csrr %0," "mtvec" : "=r" (result) ); + return (result); +} + +/********************************************************************* + * @fn __set_MTVEC + * + * @brief Set the Machine Trap-Vector Base-Address Register + * + * @param value - set mtvec value + * + * @return none + */ +void __set_MTVEC(uint32_t value) +{ + __ASM volatile ("csrw mtvec, %0" : : "r" (value) ); +} + +/********************************************************************* + * @fn __get_MSCRATCH + * + * @brief Return the Machine Seratch Register + * + * @return mscratch value + */ +uint32_t __get_MSCRATCH(void) +{ + uint32_t result; + + __ASM volatile ( "csrr %0," "mscratch" : "=r" (result) ); + return (result); +} + +/********************************************************************* + * @fn __set_MSCRATCH + * + * @brief Set the Machine Seratch Register + * + * @param value - set mscratch value + * + * @return none + */ +void __set_MSCRATCH(uint32_t value) +{ + __ASM volatile ("csrw mscratch, %0" : : "r" (value) ); +} + +/********************************************************************* + * @fn __get_MEPC + * + * @brief Return the Machine Exception Program Register + * + * @return mepc value + */ +uint32_t __get_MEPC(void) +{ + uint32_t result; + + __ASM volatile ( "csrr %0," "mepc" : "=r" (result) ); + return (result); +} + +/********************************************************************* + * @fn __set_MEPC + * + * @brief Set the Machine Exception Program Register + * + * @return mepc value + */ +void __set_MEPC(uint32_t value) +{ + __ASM volatile ("csrw mepc, %0" : : "r" (value) ); +} + +/********************************************************************* + * @fn __get_MCAUSE + * + * @brief Return the Machine Cause Register + * + * @return mcause value + */ +uint32_t __get_MCAUSE(void) +{ + uint32_t result; + + __ASM volatile ( "csrr %0," "mcause" : "=r" (result) ); + return (result); +} + +/********************************************************************* + * @fn __set_MEPC + * + * @brief Set the Machine Cause Register + * + * @return mcause value + */ +void __set_MCAUSE(uint32_t value) +{ + __ASM volatile ("csrw mcause, %0" : : "r" (value) ); +} + +/********************************************************************* + * @fn __get_MTVAL + * + * @brief Return the Machine Trap Value Register + * + * @return mtval value + */ +uint32_t __get_MTVAL(void) +{ + uint32_t result; + + __ASM volatile ( "csrr %0," "mtval" : "=r" (result) ); + return (result); +} + +/********************************************************************* + * @fn __set_MTVAL + * + * @brief Set the Machine Trap Value Register + * + * @return mtval value + */ +void __set_MTVAL(uint32_t value) +{ + __ASM volatile ("csrw mtval, %0" : : "r" (value) ); +} + +/********************************************************************* + * @fn __get_MVENDORID + * + * @brief Return Vendor ID Register + * + * @return mvendorid value + */ +uint32_t __get_MVENDORID(void) +{ + uint32_t result; + + __ASM volatile ( "csrr %0," "mvendorid" : "=r" (result) ); + return (result); +} + +/********************************************************************* + * @fn __get_MARCHID + * + * @brief Return Machine Architecture ID Register + * + * @return marchid value + */ +uint32_t __get_MARCHID(void) +{ + uint32_t result; + + __ASM volatile ( "csrr %0," "marchid" : "=r" (result) ); + return (result); +} + +/********************************************************************* + * @fn __get_MIMPID + * + * @brief Return Machine Implementation ID Register + * + * @return mimpid value + */ +uint32_t __get_MIMPID(void) +{ + uint32_t result; + + __ASM volatile ( "csrr %0," "mimpid" : "=r" (result) ); + return (result); +} + +/********************************************************************* + * @fn __get_MHARTID + * + * @brief Return Hart ID Register + * + * @return mhartid value + */ +uint32_t __get_MHARTID(void) +{ + uint32_t result; + + __ASM volatile ( "csrr %0," "mhartid" : "=r" (result) ); + return (result); +} + +/********************************************************************* + * @fn __get_SP + * + * @brief Return SP Register + * + * @return SP value + */ +uint32_t __get_SP(void) +{ + uint32_t result; + + __ASM volatile ( "mv %0," "sp" : "=r"(result) : ); + return (result); +} + diff --git a/CH5xx_ble_firmware_library/RVMSIS/core_riscv.h b/CH5xx_ble_firmware_library/RVMSIS/core_riscv.h new file mode 100644 index 0000000..287c870 --- /dev/null +++ b/CH5xx_ble_firmware_library/RVMSIS/core_riscv.h @@ -0,0 +1,381 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : core_riscv.h + * Author : WCH + * Version : V1.0.1 + * Date : 2021/10/28 + * Description : CH583 RISC-V Core Peripheral Access Layer Header File + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ +#ifndef __CORE_RV3A_H__ +#define __CORE_RV3A_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* IO definitions */ +#ifdef __cplusplus + #define __I volatile /*!< defines 'read only' permissions */ +#else + #define __I volatile const /*!< defines 'read only' permissions */ +#endif +#define __O volatile /*!< defines 'write only' permissions */ +#define __IO volatile /*!< defines 'read / write' permissions */ +#define RV_STATIC_INLINE static inline + +//typedef enum {SUCCESS = 0, ERROR = !SUCCESS} ErrorStatus; + +typedef enum +{ + DISABLE = 0, + ENABLE = !DISABLE +} FunctionalState; +typedef enum +{ + RESET = 0, + SET = !RESET +} FlagStatus, ITStatus; + +/* memory mapped structure for Program Fast Interrupt Controller (PFIC) */ +typedef struct +{ + __I uint32_t ISR[8]; // 0 + __I uint32_t IPR[8]; // 20H + __IO uint32_t ITHRESDR; // 40H + uint8_t RESERVED[4]; // 44H + __O uint32_t CFGR; // 48H + __I uint32_t GISR; // 4CH + __IO uint8_t IDCFGR[4]; // 50H + uint8_t RESERVED0[0x0C]; // 54H + __IO uint32_t FIADDRR[4]; // 60H + uint8_t RESERVED1[0x90]; // 70H + __O uint32_t IENR[8]; // 100H + uint8_t RESERVED2[0x60]; // 120H + __O uint32_t IRER[8]; // 180H + uint8_t RESERVED3[0x60]; // 1A0H + __O uint32_t IPSR[8]; // 200H + uint8_t RESERVED4[0x60]; // 220H + __O uint32_t IPRR[8]; // 280H + uint8_t RESERVED5[0x60]; // 2A0H + __IO uint32_t IACTR[8]; // 300H + uint8_t RESERVED6[0xE0]; // 320H + __IO uint8_t IPRIOR[256]; // 400H + uint8_t RESERVED7[0x810]; // 500H + __IO uint32_t SCTLR; // D10H +} PFIC_Type; + +/* memory mapped structure for SysTick */ +typedef struct +{ + __IO uint32_t CTLR; + __IO uint32_t SR; + __IO uint64_t CNT; + __IO uint64_t CMP; +} SysTick_Type; + +#define PFIC ((PFIC_Type *)0xE000E000) +#define SysTick ((SysTick_Type *)0xE000F000) + +#define PFIC_KEY1 ((uint32_t)0xFA050000) +#define PFIC_KEY2 ((uint32_t)0xBCAF0000) +#define PFIC_KEY3 ((uint32_t)0xBEEF0000) + +/* ########################## define #################################### */ +#define __nop() __asm__ volatile("nop") + +#define read_csr(reg) ({unsigned long __tmp; \ + __asm__ volatile ("csrr %0, " #reg : "=r"(__tmp)); \ + __tmp; }) + +#define write_csr(reg, val) ({ \ + if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \ + __asm__ volatile ("csrw " #reg ", %0" :: "i"(val)); \ + else \ + __asm__ volatile ("csrw " #reg ", %0" :: "r"(val)); }) + +#define PFIC_EnableAllIRQ() write_csr(0x800, 0x88) +#define PFIC_DisableAllIRQ() write_csr(0x800, 0x80) +/* ########################## PFIC functions #################################### */ + +/******************************************************************************* + * @fn PFIC_EnableIRQ + * + * @brief Enable Interrupt + * + * @param IRQn - Interrupt Numbers + */ +RV_STATIC_INLINE void PFIC_EnableIRQ(IRQn_Type IRQn) +{ + PFIC->IENR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn)&0x1F)); +} + +/******************************************************************************* + * @fn PFIC_DisableIRQ + * + * @brief Disable Interrupt + * + * @param IRQn - Interrupt Numbers + */ +RV_STATIC_INLINE void PFIC_DisableIRQ(IRQn_Type IRQn) +{ + PFIC->IRER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn)&0x1F)); + __nop(); + __nop(); +} + +/******************************************************************************* + * @fn PFIC_GetStatusIRQ + * + * @brief Get Interrupt Enable State + * + * @param IRQn - Interrupt Numbers + * + * @return 1: Interrupt Enable + * 0: Interrupt Disable + */ +RV_STATIC_INLINE uint32_t PFIC_GetStatusIRQ(IRQn_Type IRQn) +{ + return ((uint32_t)((PFIC->ISR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn)&0x1F))) ? 1 : 0)); +} + +/******************************************************************************* + * @fn PFIC_GetPendingIRQ + * + * @brief Get Interrupt Pending State + * + * @param IRQn - Interrupt Numbers + * + * @return 1: Interrupt Pending Enable + * 0: Interrupt Pending Disable + */ +RV_STATIC_INLINE uint32_t PFIC_GetPendingIRQ(IRQn_Type IRQn) +{ + return ((uint32_t)((PFIC->IPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn)&0x1F))) ? 1 : 0)); +} + +/******************************************************************************* + * @fn PFIC_SetPendingIRQ + * + * @brief Set Interrupt Pending + * + * @param IRQn - Interrupt Numbers + */ +RV_STATIC_INLINE void PFIC_SetPendingIRQ(IRQn_Type IRQn) +{ + PFIC->IPSR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn)&0x1F)); +} + +/******************************************************************************* + * @fn PFIC_ClearPendingIRQ + * + * @brief Clear Interrupt Pending + * + * @param IRQn - Interrupt Numbers + */ +RV_STATIC_INLINE void PFIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + PFIC->IPRR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn)&0x1F)); +} + +/******************************************************************************* + * @fn PFIC_GetActive + * + * @brief Get Interrupt Active State + * + * @param IRQn - Interrupt Numbers + * + * @return 1: Interrupt Active + * 0: Interrupt No Active. + */ +RV_STATIC_INLINE uint32_t PFIC_GetActive(IRQn_Type IRQn) +{ + return ((uint32_t)((PFIC->IACTR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn)&0x1F))) ? 1 : 0)); +} + +/******************************************************************************* + * @fn PFIC_SetPriority + * + * @brief Set Interrupt Priority + * + * @param IRQn - Interrupt Numbers + * @param priority - bit7: pre-emption priority + * bit6-bit4: subpriority + */ +RV_STATIC_INLINE void PFIC_SetPriority(IRQn_Type IRQn, uint8_t priority) +{ + PFIC->IPRIOR[(uint32_t)(IRQn)] = priority; +} + +/********************************************************************* + * @fn PFIC_EnableFastINT0 + * + * @brief Set fast Interrupt 0 + * + * @param IRQn - Interrupt Numbers + * @param addr - interrupt service addr + */ +RV_STATIC_INLINE void PFIC_EnableFastINT0(IRQn_Type IRQn, uint32_t addr) +{ + PFIC->IDCFGR[0] = IRQn; + PFIC->FIADDRR[0] = (addr & 0xFFFFFFFE) | 1; +} + +/********************************************************************* + * @fn PFIC_EnableFastINT1 + * + * @brief Set fast Interrupt 1 + * + * @param IRQn - Interrupt Numbers + * @param addr - interrupt service addr + */ +RV_STATIC_INLINE void PFIC_EnableFastINT1(IRQn_Type IRQn, uint32_t addr) +{ + PFIC->IDCFGR[1] = IRQn; + PFIC->FIADDRR[1] = (addr & 0xFFFFFFFE) | 1; +} + +/********************************************************************* + * @fn PFIC_EnableFastINT2 + * + * @brief Set fast Interrupt 2 + * + * @param IRQn - Interrupt Numbers + * @param addr - interrupt service addr + */ +RV_STATIC_INLINE void PFIC_EnableFastINT2(IRQn_Type IRQn, uint32_t addr) +{ + PFIC->IDCFGR[2] = IRQn; + PFIC->FIADDRR[2] = (addr & 0xFFFFFFFE) | 1; +} + +/********************************************************************* + * @fn PFIC_EnableFastINT3 + * + * @brief Set fast Interrupt 3 + * + * @param IRQn - Interrupt Numbers + * @param addr - interrupt service addr + */ +RV_STATIC_INLINE void PFIC_EnableFastINT3(IRQn_Type IRQn, uint32_t addr) +{ + PFIC->IDCFGR[3] = IRQn; + PFIC->FIADDRR[3] = (addr & 0xFFFFFFFE) | 1; +} + +/********************************************************************* + * @fn PFIC_DisableFastINT0 + * + * @brief Disable fast Interrupt 0 + */ +RV_STATIC_INLINE void PFIC_DisableFastINT0(void) +{ + PFIC->FIADDRR[0] = PFIC->FIADDRR[0] & 0xFFFFFFFE; +} + +/********************************************************************* + * @fn PFIC_DisableFastINT1 + * + * @brief Disable fast Interrupt 1 + */ +RV_STATIC_INLINE void PFIC_DisableFastINT1(void) +{ + PFIC->FIADDRR[1] = PFIC->FIADDRR[1] & 0xFFFFFFFE; +} + +/********************************************************************* + * @fn PFIC_DisableFastINT2 + * + * @brief Disable fast Interrupt 2 + */ +RV_STATIC_INLINE void PFIC_DisableFastINT2(void) +{ + PFIC->FIADDRR[2] = PFIC->FIADDRR[2] & 0xFFFFFFFE; +} + +/********************************************************************* + * @fn PFIC_DisableFastINT3 + * + * @brief Disable fast Interrupt 3 + */ +RV_STATIC_INLINE void PFIC_DisableFastINT3(void) +{ + PFIC->FIADDRR[3] = PFIC->FIADDRR[3] & 0xFFFFFFFE; +} + +/********************************************************************* + * @fn __SEV + * + * @brief Wait for Events + */ +__attribute__((always_inline)) RV_STATIC_INLINE void __SEV(void) +{ + PFIC->SCTLR |= (1 << 3); +} + +/********************************************************************* + * @fn __WFI + * + * @brief Wait for Interrupt + */ +__attribute__((always_inline)) RV_STATIC_INLINE void __WFI(void) +{ + PFIC->SCTLR &= ~(1 << 3); // wfi + __asm__ volatile("wfi"); +} + +/********************************************************************* + * @fn __WFE + * + * @brief Wait for Events + */ +__attribute__((always_inline)) RV_STATIC_INLINE void __WFE(void) +{ + PFIC->SCTLR |= (1 << 3) | (1 << 5); // (wfi->wfe)+(__sev) + __asm__ volatile("wfi"); + PFIC->SCTLR |= (1 << 3); + __asm__ volatile("wfi"); +} + +/********************************************************************* + * @fn PFIC_SystemReset + * + * @brief Initiate a system reset request + */ +RV_STATIC_INLINE void PFIC_SystemReset(void) +{ + PFIC->CFGR = PFIC_KEY3 | (1 << 7); +} + +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFFFFFFFFFFF) +#define SysTick_CTLR_INIT (1 << 5) +#define SysTick_CTLR_MODE (1 << 4) +#define SysTick_CTLR_STRE (1 << 3) +#define SysTick_CTLR_STCLK (1 << 2) +#define SysTick_CTLR_STIE (1 << 1) +#define SysTick_CTLR_STE (1 << 0) + +#define SysTick_SR_CNTIF (1 << 0) + +RV_STATIC_INLINE uint32_t SysTick_Config(uint64_t ticks) +{ + if((ticks - 1) > SysTick_LOAD_RELOAD_Msk) + return (1); /* Reload value impossible */ + + SysTick->CMP = ticks - 1; /* set reload register */ + PFIC_EnableIRQ(SysTick_IRQn); + SysTick->CTLR = SysTick_CTLR_INIT | + SysTick_CTLR_STRE | + SysTick_CTLR_STCLK | + SysTick_CTLR_STIE | + SysTick_CTLR_STE; /* Enable SysTick IRQ and SysTick Timer */ + return (0); /* Function successful */ +} + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_RV3A_H__ */ diff --git a/CH5xx_ble_firmware_library/Startup/startup_CH583.S b/CH5xx_ble_firmware_library/Startup/startup_CH583.S new file mode 100644 index 0000000..b408515 --- /dev/null +++ b/CH5xx_ble_firmware_library/Startup/startup_CH583.S @@ -0,0 +1,188 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : startup_ch58x.s + * Author : WCH + * Version : V1.0.0 + * Date : 2021/02/25 + * Description : + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + + .section .init,"ax",@progbits + .global _start + .align 1 +_start: + j handle_reset + + .section .vector,"ax",@progbits + .align 1 +_vector_base: + .option norvc; + + .word 0 + .word 0 + .word NMI_Handler /* NMI Handler */ + .word HardFault_Handler /* Hard Fault Handler */ + .word 0xF5F9BDA9 + .word Ecall_M_Mode_Handler /* 5 */ + .word 0 + .word 0 + .word Ecall_U_Mode_Handler /* 8 */ + .word Break_Point_Handler /* 9 */ + .word 0 + .word 0 + .word SysTick_Handler /* SysTick Handler */ + .word 0 + .word SW_Handler /* SW Handler */ + .word 0 + /* External Interrupts */ + .word TMR0_IRQHandler /* 0: TMR0 */ + .word GPIOA_IRQHandler /* GPIOA */ + .word GPIOB_IRQHandler /* GPIOB */ + .word SPI0_IRQHandler /* SPI0 */ + .word BB_IRQHandler /* BLEB */ + .word LLE_IRQHandler /* BLEL */ + .word USB_IRQHandler /* USB */ + .word USB2_IRQHandler /* USB2 */ + .word TMR1_IRQHandler /* TMR1 */ + .word TMR2_IRQHandler /* TMR2 */ + .word UART0_IRQHandler /* UART0 */ + .word UART1_IRQHandler /* UART1 */ + .word RTC_IRQHandler /* RTC */ + .word ADC_IRQHandler /* ADC */ + .word I2C_IRQHandler /* I2C */ + .word PWMX_IRQHandler /* PWMX */ + .word TMR3_IRQHandler /* TMR3 */ + .word UART2_IRQHandler /* UART2 */ + .word UART3_IRQHandler /* UART3 */ + .word WDOG_BAT_IRQHandler /* WDOG_BAT */ + + .option rvc; + + .section .vector_handler, "ax", @progbits + .weak NMI_Handler + .weak HardFault_Handler + .weak Ecall_M_Mode_Handler + .weak Ecall_U_Mode_Handler + .weak Break_Point_Handler + .weak SysTick_Handler + .weak SW_Handler + .weak TMR0_IRQHandler + .weak GPIOA_IRQHandler + .weak GPIOB_IRQHandler + .weak SPI0_IRQHandler + .weak BB_IRQHandler + .weak LLE_IRQHandler + .weak USB_IRQHandler + .weak USB2_IRQHandler + .weak TMR1_IRQHandler + .weak TMR2_IRQHandler + .weak UART0_IRQHandler + .weak UART1_IRQHandler + .weak RTC_IRQHandler + .weak ADC_IRQHandler + .weak I2C_IRQHandler + .weak PWMX_IRQHandler + .weak TMR3_IRQHandler + .weak UART2_IRQHandler + .weak UART3_IRQHandler + .weak WDOG_BAT_IRQHandler + +NMI_Handler: 1: j 1b +HardFault_Handler: 1: j 1b +Ecall_M_Mode_Handler: 1: j 1b +Ecall_U_Mode_Handler: 1: j 1b +Break_Point_Handler: 1: j 1b +SysTick_Handler: 1: j 1b +SW_Handler: 1: j 1b +TMR0_IRQHandler: 1: j 1b +GPIOA_IRQHandler: 1: j 1b +GPIOB_IRQHandler: 1: j 1b +SPI0_IRQHandler: 1: j 1b +BB_IRQHandler: 1: j 1b +LLE_IRQHandler: 1: j 1b +USB_IRQHandler: 1: j 1b +USB2_IRQHandler: 1: j 1b +TMR1_IRQHandler: 1: j 1b +TMR2_IRQHandler: 1: j 1b +UART0_IRQHandler: 1: j 1b +UART1_IRQHandler: 1: j 1b +RTC_IRQHandler: 1: j 1b +ADC_IRQHandler: 1: j 1b +I2C_IRQHandler: 1: j 1b +PWMX_IRQHandler: 1: j 1b +TMR3_IRQHandler: 1: j 1b +UART2_IRQHandler: 1: j 1b +UART3_IRQHandler: 1: j 1b +WDOG_BAT_IRQHandler: 1: j 1b + + .section .handle_reset,"ax",@progbits + .weak handle_reset + .align 1 +handle_reset: +.option push +.option norelax + la gp, __global_pointer$ +.option pop +1: + la sp, _eusrstack + +/* Load highcode code section from flash to RAM */ +2: + la a0, _highcode_lma + la a1, _highcode_vma_start + la a2, _highcode_vma_end + bgeu a1, a2, 2f +1: + lw t0, (a0) + sw t0, (a1) + addi a0, a0, 4 + addi a1, a1, 4 + bltu a1, a2, 1b + +/* Load data section from flash to RAM */ +2: + la a0, _data_lma + la a1, _data_vma + la a2, _edata + bgeu a1, a2, 2f +1: + lw t0, (a0) + sw t0, (a1) + addi a0, a0, 4 + addi a1, a1, 4 + bltu a1, a2, 1b +2: + /* clear bss section */ + la a0, _sbss + la a1, _ebss + bgeu a0, a1, 2f +1: + sw zero, (a0) + addi a0, a0, 4 + bltu a0, a1, 1b +2: + /* ˮ߿λ & ̬Ԥλ */ + li t0, 0x1f + csrw 0xbc0, t0 + /* ǶжϡӲѹջ */ + li t0, 0x3 + csrw 0x804, t0 + + li t0, 0x88 + csrs mstatus, t0 + la t0, _vector_base + + /* ģʽΪԵַģʽ */ + ori t0, t0, 3 + csrw mtvec, t0 + + la t0, main + csrw mepc, t0 + + + mret + + diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_adc.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_adc.c new file mode 100644 index 0000000..de86726 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_adc.c @@ -0,0 +1,235 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_adc.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn ADC_DataCalib_Rough + * + * @brief ݴֵ,ȡƫֵ,ADCô˺ȡУ׼ֵ + * + * @param none + * + * @return ƫ + */ +signed short ADC_DataCalib_Rough(void) // ݴֵ,ȡƫֵ +{ + uint16_t i; + uint32_t sum = 0; + uint8_t ch = 0; // ͨ + + ch = R8_ADC_CHANNEL; + + ADC_ChannelCfg(1); // ADCУ׼ͨѡͨ1 + R8_ADC_CFG |= RB_ADC_OFS_TEST; // ģʽ + R8_ADC_CONVERT = RB_ADC_START; + while(R8_ADC_CONVERT & RB_ADC_START); + for(i = 0; i < 16; i++) + { + R8_ADC_CONVERT = RB_ADC_START; + while(R8_ADC_CONVERT & RB_ADC_START); + sum += (~R16_ADC_DATA) & RB_ADC_DATA; + } + sum = (sum + 8) >> 4; + R8_ADC_CFG &= ~RB_ADC_OFS_TEST; // رղģʽ + + R8_ADC_CHANNEL = ch; + + return (2048 - sum); +} + +/********************************************************************* + * @fn ADC_ExtSingleChSampInit + * + * @brief ⲿźŵͨʼ + * + * @param sp - refer to ADC_SampClkTypeDef + * @param ga - refer to ADC_SignalPGATypeDef + * + * @return none + */ +void ADC_ExtSingleChSampInit(ADC_SampClkTypeDef sp, ADC_SignalPGATypeDef ga) +{ + R8_TKEY_CFG &= ~RB_TKEY_PWR_ON; + R8_ADC_CFG = RB_ADC_POWER_ON | RB_ADC_BUF_EN | (sp << 6) | (ga << 4); +} + +/********************************************************************* + * @fn ADC_ExtDiffChSampInit + * + * @brief ⲿźŲͨʼ + * + * @param sp - refer to ADC_SampClkTypeDef + * @param ga - refer to ADC_SignalPGATypeDef + * + * @return none + */ +void ADC_ExtDiffChSampInit(ADC_SampClkTypeDef sp, ADC_SignalPGATypeDef ga) +{ + R8_TKEY_CFG &= ~RB_TKEY_PWR_ON; + R8_ADC_CFG = RB_ADC_POWER_ON | RB_ADC_DIFF_EN | (sp << 6) | (ga << 4); +} + +/********************************************************************* + * @fn ADC_InterTSSampInit + * + * @brief ¶ȴʼ + * + * @param none + * + * @return none + */ +void ADC_InterTSSampInit(void) +{ + R8_TKEY_CFG &= ~RB_TKEY_PWR_ON; + R8_TEM_SENSOR = RB_TEM_SEN_PWR_ON; + R8_ADC_CHANNEL = CH_INTE_VTEMP; + R8_ADC_CFG = RB_ADC_POWER_ON | RB_ADC_DIFF_EN | (3 << 4); +} + +/********************************************************************* + * @fn ADC_InterBATSampInit + * + * @brief õصѹʼ + * + * @param none + * + * @return none + */ +void ADC_InterBATSampInit(void) +{ + R8_TKEY_CFG &= ~RB_TKEY_PWR_ON; + R8_ADC_CHANNEL = CH_INTE_VBAT; + R8_ADC_CFG = RB_ADC_POWER_ON | RB_ADC_BUF_EN | (0 << 4); // ʹ-12dBģʽ +} + +/********************************************************************* + * @fn TouchKey_ChSampInit + * + * @brief ͨʼ + * + * @param none + * + * @return none + */ +void TouchKey_ChSampInit(void) +{ + R8_ADC_CFG = RB_ADC_POWER_ON | RB_ADC_BUF_EN | (2 << 4); + R8_TKEY_CFG |= RB_TKEY_PWR_ON; +} + +/********************************************************************* + * @fn ADC_ExcutSingleConver + * + * @brief ADCִеת + * + * @param none + * + * @return ADCת + */ +uint16_t ADC_ExcutSingleConver(void) +{ + R8_ADC_CONVERT = RB_ADC_START; + while(R8_ADC_CONVERT & RB_ADC_START); + + return (R16_ADC_DATA & RB_ADC_DATA); +} + +/********************************************************************* + * @fn TouchKey_ExcutSingleConver + * + * @brief TouchKeyת + * + * @param charg - Touchkeyʱ,5bitsЧ, t=charg*Tadc + * @param disch - Touchkeyŵʱ,3bitsЧ, t=disch*Tadc + * + * @return ǰTouchKeyЧ + */ +uint16_t TouchKey_ExcutSingleConver(uint8_t charg, uint8_t disch) +{ + R8_TKEY_COUNT = (disch << 5) | (charg & 0x1f); + R8_TKEY_CONVERT = RB_TKEY_START; + while(R8_TKEY_CONVERT & RB_TKEY_START); + return (R16_ADC_DATA & RB_ADC_DATA); +} + +/********************************************************************* + * @fn ADC_AutoConverCycle + * + * @brief ADC + * + * @param cycle - ڼ㷽Ϊ(256-cycle)*16*Tsys + * + * @return none + */ +void ADC_AutoConverCycle(uint8_t cycle) +{ + R8_ADC_AUTO_CYCLE = cycle; +} + +/********************************************************************* + * @fn ADC_DMACfg + * + * @brief DMA + * + * @param s - ǷDMA + * @param startAddr - DMA ʼַ + * @param endAddr - DMA ַ + * @param m - DMAģʽ + * + * @return none + */ +void ADC_DMACfg(uint8_t s, uint16_t startAddr, uint16_t endAddr, ADC_DMAModeTypeDef m) +{ + if(s == DISABLE) + { + R8_ADC_CTRL_DMA &= ~(RB_ADC_DMA_ENABLE | RB_ADC_IE_DMA_END); + } + else + { + R16_ADC_DMA_BEG = startAddr; + R16_ADC_DMA_END = endAddr; + if(m) + { + R8_ADC_CTRL_DMA |= RB_ADC_DMA_LOOP | RB_ADC_IE_DMA_END | RB_ADC_DMA_ENABLE; + } + else + { + R8_ADC_CTRL_DMA &= ~RB_ADC_DMA_LOOP; + R8_ADC_CTRL_DMA |= RB_ADC_IE_DMA_END | RB_ADC_DMA_ENABLE; + } + } +} + +/********************************************************************* + * @fn adc_to_temperature_celsius + * + * @brief Convert ADC value to temperature(Celsius) + * + * @param adc_val - adc value + * + * @return temperature (Celsius) + */ + +int adc_to_temperature_celsius(uint16_t adc_val) +{ + uint32_t C25 = 0; + int temp; + + C25 = (*((PUINT32)ROM_CFG_TMP_25C)); + + /* current temperature = standard temperature + (adc deviation * adc linearity coefficient) */ + temp = (((C25 >> 16) & 0xFFFF) ? ((C25 >> 16) & 0xFFFF) : 25) + \ + (adc_val - ((int)(C25 & 0xFFFF))) * 10 / 27; + + return (temp); +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_clk.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_clk.c new file mode 100644 index 0000000..a1d345f --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_clk.c @@ -0,0 +1,477 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_clk.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn LClk32K_Select + * + * @brief 32K ƵʱԴ + * + * @param hc - ѡ32Kʹڲⲿ + * + * @return none + */ +void LClk32K_Select(LClk32KTypeDef hc) +{ + uint8_t cfg = R8_CK32K_CONFIG; + + if(hc == Clk32K_LSI) + { + cfg &= ~RB_CLK_OSC32K_XT; + } + else + { + cfg |= RB_CLK_OSC32K_XT; + } + + sys_safe_access_enable(); + R8_CK32K_CONFIG = cfg; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn HSECFG_Current + * + * @brief HSE ƫõ + * + * @param c - 75%,100%,125%,150% + * + * @return none + */ +void HSECFG_Current(HSECurrentTypeDef c) +{ + uint8_t x32M_c; + + x32M_c = R8_XT32M_TUNE; + x32M_c = (x32M_c & 0xfc) | (c & 0x03); + + sys_safe_access_enable(); + R8_XT32M_TUNE = x32M_c; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn HSECFG_Capacitance + * + * @brief HSE ص + * + * @param c - refer to HSECapTypeDef + * + * @return none + */ +void HSECFG_Capacitance(HSECapTypeDef c) +{ + uint8_t x32M_c; + + x32M_c = R8_XT32M_TUNE; + x32M_c = (x32M_c & 0x8f) | (c << 4); + + sys_safe_access_enable(); + R8_XT32M_TUNE = x32M_c; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn LSECFG_Current + * + * @brief LSE ƫõ + * + * @param c - 70%,100%,140%,200% + * + * @return none + */ +void LSECFG_Current(LSECurrentTypeDef c) +{ + uint8_t x32K_c; + + x32K_c = R8_XT32K_TUNE; + x32K_c = (x32K_c & 0xfc) | (c & 0x03); + + sys_safe_access_enable(); + R8_XT32K_TUNE = x32K_c; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn LSECFG_Capacitance + * + * @brief LSE ص + * + * @param c - refer to LSECapTypeDef + * + * @return none + */ +void LSECFG_Capacitance(LSECapTypeDef c) +{ + uint8_t x32K_c; + + x32K_c = R8_XT32K_TUNE; + x32K_c = (x32K_c & 0x0f) | (c << 4); + + sys_safe_access_enable(); + R8_XT32K_TUNE = x32K_c; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn Calibration_LSI + * + * @brief У׼ڲ32Kʱ + * + * @param cali_Lv - У׼ȼѡ Level_32 - ʱ 1.2ms 1000ppm (32M Ƶ) 1100ppm (60M Ƶ) + * Level_64 - ʱ 2.2ms 800ppm (32M Ƶ) 1000ppm (60M Ƶ) + * Level_128 - ʱ 4.2ms 600ppm (32M Ƶ) 800ppm (60M Ƶ) + * + * @return none + */ +void Calibration_LSI(Cali_LevelTypeDef cali_Lv) +{ + UINT32 i; + INT32 cnt_offset; + UINT8 retry = 0; + INT32 freq_sys; + + freq_sys = GetSysClock(); + + sys_safe_access_enable(); + R8_CK32K_CONFIG |= RB_CLK_OSC32K_FILT; + R8_CK32K_CONFIG &= ~RB_CLK_OSC32K_FILT; + sys_safe_access_enable(); + R8_XT32K_TUNE &= ~3; + R8_XT32K_TUNE |= 1; + + // ֵ + sys_safe_access_enable(); + R8_OSC_CAL_CTRL &= ~RB_OSC_CNT_TOTAL; + R8_OSC_CAL_CTRL |= 1; + sys_safe_access_enable(); + R8_OSC_CAL_CTRL |= RB_OSC_CNT_EN; + R16_OSC_CAL_CNT |= RB_OSC_CAL_OV_CLR; + while( (R8_OSC_CAL_CTRL &RB_OSC_CNT_EN) == 0 ) + { + sys_safe_access_enable(); + R8_OSC_CAL_CTRL |= RB_OSC_CNT_EN; + } + while(1) + { + while(!(R8_OSC_CAL_CTRL & RB_OSC_CNT_HALT)); + i = R16_OSC_CAL_CNT; // ڶ + while(R8_OSC_CAL_CTRL & RB_OSC_CNT_HALT); + R16_OSC_CAL_CNT |= RB_OSC_CAL_OV_CLR; + while(!(R8_OSC_CAL_CTRL & RB_OSC_CNT_HALT)); + i = R16_OSC_CAL_CNT; // ʵʱУ׼ֵ + cnt_offset = (i & 0x3FFF) + R8_OSC_CAL_OV_CNT * 0x3FFF - 2000 * (freq_sys / 1000) / CAB_LSIFQ; + if(((cnt_offset > -37 * (freq_sys / 1000) / CAB_LSIFQ) && (cnt_offset < 37 * (freq_sys / 1000) / CAB_LSIFQ)) || retry > 2) + { + if(retry) + break; + } + retry++; + cnt_offset = (cnt_offset > 0) ? (((cnt_offset * 2) / (74 * (freq_sys/1000) / 60000)) + 1) / 2 : (((cnt_offset * 2) / (74 * (freq_sys/1000) / 60000 )) - 1) / 2; + sys_safe_access_enable(); + R16_INT32K_TUNE += cnt_offset; + } + + // ϸ + // ϸ󣬶2βֵΪжһΣֻһ + while(!(R8_OSC_CAL_CTRL & RB_OSC_CNT_HALT)); + i = R16_OSC_CAL_CNT; // ڶ + R16_OSC_CAL_CNT |= RB_OSC_CAL_OV_CLR; + sys_safe_access_enable(); + R8_OSC_CAL_CTRL &= ~RB_OSC_CNT_TOTAL; + R8_OSC_CAL_CTRL |= cali_Lv; + while( (R8_OSC_CAL_CTRL&0x07) != cali_Lv ) + { + sys_safe_access_enable(); + R8_OSC_CAL_CTRL |= cali_Lv; + } + while(R8_OSC_CAL_CTRL & RB_OSC_CNT_HALT); + while(!(R8_OSC_CAL_CTRL & RB_OSC_CNT_HALT)); + i = R16_OSC_CAL_CNT; // ʵʱУ׼ֵ + + cnt_offset = (i & 0x3FFF) + R8_OSC_CAL_OV_CNT * 0x3FFF - 4000 * (1 << cali_Lv) * (freq_sys / 1000000) / 256 * 1000/(CAB_LSIFQ/256) ; + cnt_offset = (cnt_offset > 0) ? ((((cnt_offset * 2*(100 )) / (1366 * ((1 << cali_Lv)/8) * (freq_sys/1000) / 60000)) + 1) / 2)<<5 : ((((cnt_offset * 2*(100)) / (1366 * ((1 << cali_Lv)/8) * (freq_sys/1000) / 60000)) - 1) / 2)<<5; + sys_safe_access_enable(); + R16_INT32K_TUNE += cnt_offset; + R8_OSC_CAL_CTRL &= ~RB_OSC_CNT_EN; +} + +/********************************************************************* + * @fn RTCInitTime + * + * @brief RTCʱӳʼǰʱ + * + * @param y - ꣬MAX_Y = BEGYEAR + 44 + * @param mon - £MAX_MON = 12 + * @param d - գMAX_D = 31 + * @param h - СʱMAX_H = 23 + * @param m - ÷ӣMAX_M = 59 + * @param s - 룬MAX_S = 59 + * + * @return none + */ +void RTC_InitTime(uint16_t y, uint16_t mon, uint16_t d, uint16_t h, uint16_t m, uint16_t s) +{ + uint32_t t; + uint16_t year, month, day, sec2, t32k; + volatile uint8_t clk_pin; + + year = y; + month = mon; + day = 0; + while(year > BEGYEAR) + { + day += YearLength(year - 1); + year--; + } + while(month > 1) + { + day += monthLength(IsLeapYear(y), month - 2); + month--; + } + + day += d - 1; + sec2 = (h % 24) * 1800 + m * 30 + s / 2; + t32k = (s & 1) ? (0x8000) : (0); + t = sec2; + t = t << 16 | t32k; + + do + { + clk_pin = (R8_CK32K_CONFIG & RB_32K_CLK_PIN); + } while(clk_pin != (R8_CK32K_CONFIG & RB_32K_CLK_PIN)); + if(!clk_pin) + { + while(!clk_pin) + { + do + { + clk_pin = (R8_CK32K_CONFIG & RB_32K_CLK_PIN); + } while(clk_pin != (R8_CK32K_CONFIG & RB_32K_CLK_PIN)); + } + } + + sys_safe_access_enable(); + R32_RTC_TRIG = day; + R8_RTC_MODE_CTRL |= RB_RTC_LOAD_HI; + while((R32_RTC_TRIG & 0x3FFF) != (R32_RTC_CNT_DAY & 0x3FFF)); + sys_safe_access_enable(); + R32_RTC_TRIG = t; + R8_RTC_MODE_CTRL |= RB_RTC_LOAD_LO; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn RTC_GetTime + * + * @brief ȡǰʱ + * + * @param py - ȡ꣬MAX_Y = BEGYEAR + 44 + * @param pmon - ȡ£MAX_MON = 12 + * @param pd - ȡգMAX_D = 31 + * @param ph - ȡСʱMAX_H = 23 + * @param pm - ȡķӣMAX_M = 59 + * @param ps - ȡ룬MAX_S = 59 + * + * @return none + */ +void RTC_GetTime(uint16_t *py, uint16_t *pmon, uint16_t *pd, uint16_t *ph, uint16_t *pm, uint16_t *ps) +{ + uint32_t t; + uint16_t day, sec2, t32k; + + day = R32_RTC_CNT_DAY & 0x3FFF; + sec2 = R16_RTC_CNT_2S; + t32k = R16_RTC_CNT_32K; + + t = sec2 * 2 + ((t32k < 0x8000) ? 0 : 1); + + *py = BEGYEAR; + while(day >= YearLength(*py)) + { + day -= YearLength(*py); + (*py)++; + } + + *pmon = 0; + while(day >= monthLength(IsLeapYear(*py), *pmon)) + { + day -= monthLength(IsLeapYear(*py), *pmon); + (*pmon)++; + } + (*pmon)++; + *pd = day + 1; + *ph = t / 3600; + *pm = t % 3600 / 60; + *ps = t % 60; +} + +/********************************************************************* + * @fn RTC_SetCycle32k + * + * @brief LSE/LSIʱӣõǰRTC + * + * @param cyc - ڼֵMAX_CYC = 0xA8BFFFFF = 2831155199 + * + * @return none + */ +void RTC_SetCycle32k(uint32_t cyc) +{ + volatile uint8_t clk_pin; + + do + { + clk_pin = (R8_CK32K_CONFIG & RB_32K_CLK_PIN); + } while((clk_pin != (R8_CK32K_CONFIG & RB_32K_CLK_PIN)) || (!clk_pin)); + + sys_safe_access_enable(); + R32_RTC_TRIG = cyc; + R8_RTC_MODE_CTRL |= RB_RTC_LOAD_LO; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn RTC_GetCycle32k + * + * @brief LSE/LSIʱӣȡǰRTC + * + * @param none + * + * @return ǰMAX_CYC = 0xA8BFFFFF = 2831155199 + */ +uint32_t RTC_GetCycle32k(void) +{ + volatile uint32_t i; + + do + { + i = R32_RTC_CNT_32K; + } while(i != R32_RTC_CNT_32K); + + return (i); +} + +/********************************************************************* + * @fn RTC_TMRFunCfg + * + * @brief RTCʱģʽãעⶨʱ׼̶Ϊ32768Hz + * + * @param t - refer to RTC_TMRCycTypeDef + * + * @return none + */ +void RTC_TMRFunCfg(RTC_TMRCycTypeDef t) +{ + sys_safe_access_enable(); + R8_RTC_MODE_CTRL &= ~(RB_RTC_TMR_EN | RB_RTC_TMR_MODE); + sys_safe_access_enable(); + R8_RTC_MODE_CTRL |= RB_RTC_TMR_EN | (t); + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn RTC_TRIGFunCfg + * + * @brief RTCʱ䴥ģʽ + * + * @param cyc - ԵǰʱĴʱ䣬LSE/LSIʱ + * + * @return none + */ +void RTC_TRIGFunCfg(uint32_t cyc) +{ + uint32_t t; + + t = RTC_GetCycle32k() + cyc; + if(t > 0xA8C00000) + { + t -= 0xA8C00000; + } + + sys_safe_access_enable(); + R32_RTC_TRIG = t; + R8_RTC_MODE_CTRL |= RB_RTC_TRIG_EN; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn RTC_ModeFunDisable + * + * @brief RTC ģʽܹر + * + * @param m - Ҫرյĵǰģʽ + * + * @return none + */ +void RTC_ModeFunDisable(RTC_MODETypeDef m) +{ + uint8_t i = 0; + + if(m == RTC_TRIG_MODE) + { + i |= RB_RTC_TRIG_EN; + } + else if(m == RTC_TMR_MODE) + { + i |= RB_RTC_TMR_EN; + } + + sys_safe_access_enable(); + R8_RTC_MODE_CTRL &= ~(i); + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn RTC_GetITFlag + * + * @brief ȡRTCжϱ־ + * + * @param f - refer to RTC_EVENTTypeDef + * + * @return жϱ־״̬ + */ +uint8_t RTC_GetITFlag(RTC_EVENTTypeDef f) +{ + if(f == RTC_TRIG_EVENT) + { + return (R8_RTC_FLAG_CTRL & RB_RTC_TRIG_FLAG); + } + else + { + return (R8_RTC_FLAG_CTRL & RB_RTC_TMR_FLAG); + } +} + +/********************************************************************* + * @fn RTC_ClearITFlag + * + * @brief RTCжϱ־ + * + * @param f - refer to RTC_EVENTTypeDef + * + * @return none + */ +void RTC_ClearITFlag(RTC_EVENTTypeDef f) +{ + switch(f) + { + case RTC_TRIG_EVENT: + R8_RTC_FLAG_CTRL = RB_RTC_TRIG_CLR; + break; + case RTC_TMR_EVENT: + R8_RTC_FLAG_CTRL = RB_RTC_TMR_CLR; + break; + default: + break; + } +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_flash.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_flash.c new file mode 100644 index 0000000..6c726ec --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_flash.c @@ -0,0 +1,166 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_flash.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/* RESET_EN */ +#define RESET_Enable 0x00000008 +#define RESET_Disable 0xFFFFFFF7 + +/* LOCKUP_RST_EN */ +#define UART_NO_KEY_Enable 0x00000100 +#define UART_NO_KEY_Disable 0xFFFFFEFF + +/* BOOT_PIN */ +#define BOOT_PIN_PB22 0x00000200 +#define BOOT_PIN_PB11 0xFFFFFDFF + +/* FLASH_WRProt */ +#define FLASH_WRProt 0xFFF003FF + +/********************************************************************* + * @fn FLASH_ROM_READ + * + * @brief Read Flash + * + * @param StartAddr - read address + * @param Buffer - read buffer + * @param len - read len + * + * @return none + */ +void FLASH_ROM_READ(uint32_t StartAddr, void *Buffer, uint32_t len) +{ + uint32_t i, Length = (len + 3) >> 2; + uint32_t *pCode = (uint32_t *)StartAddr; + uint32_t *pBuf = (uint32_t *)Buffer; + + for(i = 0; i < Length; i++) + { + *pBuf++ = *pCode++; + } +} + +/********************************************************************* + * @fn UserOptionByteConfig + * + * @brief Configure User Option Byte.ڵûЧЧ,ÿ¼ֻ޸һ + * (ʹøúʹùٷṩ.SļͬʱøúϵߵԽӿĬϹر) + * + * @param RESET_EN - ⲿλʹ + * @param BOOT_PIN - ENABLE-ʹĬboot-PB22,DISABLE-ʹboot-PB11 + * @param UART_NO_KEY_EN - ⰴʹ + * @param FLASHProt_Size - дС(λ4K) + * + * @return 0-Success, 1-Err + */ +uint8_t UserOptionByteConfig(FunctionalState RESET_EN, FunctionalState BOOT_PIN, FunctionalState UART_NO_KEY_EN, + uint32_t FLASHProt_Size) +{ + uint32_t s, t; + + FLASH_ROM_READ(0x14, &s, 4); + + if(s == 0xF5F9BDA9) + { + s = 0; + FLASH_EEPROM_CMD(CMD_GET_ROM_INFO, 0x7EFFC, &s, 4); + s &= 0xFF; + + if(RESET_EN == ENABLE) + s |= RESET_Enable; + else + s &= RESET_Disable; + + /* bit[7:0]-bit[31-24] */ + s |= ((~(s << 24)) & 0xFF000000); //8λ Ϣȡ + + if(BOOT_PIN == ENABLE) + s |= BOOT_PIN_PB22; + if(UART_NO_KEY_EN == ENABLE) + s |= UART_NO_KEY_Enable; + + /* bit[23-10] */ + s &= 0xFF0003FF; + s |= ((FLASHProt_Size << 10) | (5 << 20)) & 0x00FFFC00; + + /*Write user option byte*/ + FLASH_ROM_WRITE(0x14, &s, 4); + + /* Verify user option byte */ + FLASH_ROM_READ(0x14, &t, 4); + + if(s == t) + return 0; + else + return 1; + } + + return 1; +} + +/********************************************************************* + * @fn UserOptionByteClose_SWD + * + * @brief ߵԽӿڣֲֵ.ڵûЧЧ,ÿ¼ֻ޸һ + * (ʹøúʹùٷṩ.SļͬʱøúϵߵԽӿĬϹر) + * + * @return 0-Success, 1-Err + */ +uint8_t UserOptionByteClose_SWD(void) +{ + uint32_t s, t; + + FLASH_ROM_READ(0x14, &s, 4); + + if(s == 0xF5F9BDA9) + { + FLASH_EEPROM_CMD(CMD_GET_ROM_INFO, 0x7EFFC, &s, 4); + + s &= ~((1 << 4) | (1 << 7)); //õԹܣ SPIдFLASH + + /* bit[7:0]-bit[31-24] */ + s &= 0x00FFFFFF; + s |= ((~(s << 24)) & 0xFF000000); //8λ Ϣȡ + + /*Write user option byte*/ + FLASH_ROM_WRITE(0x14, &s, 4); + + /* Verify user option byte */ + FLASH_ROM_READ(0x14, &t, 4); + + if(s == t) + return 0; + else + return 1; + } + + return 1; +} + +/********************************************************************* + * @fn UserOptionByte_Active + * + * @brief ûЧִкԶλ + * + * @return 0-Success, 1-Err + */ +void UserOptionByte_Active(void) +{ + FLASH_ROM_SW_RESET(); + sys_safe_access_enable(); + R16_INT32K_TUNE = 0xFFFF; + sys_safe_access_enable(); + R8_RST_WDOG_CTRL |= RB_SOFTWARE_RESET; + sys_safe_access_disable(); + while(1); +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_gpio.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_gpio.c new file mode 100644 index 0000000..f0469b2 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_gpio.c @@ -0,0 +1,263 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_gpio.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn GPIOA_ModeCfg + * + * @brief GPIOA˿ģʽ + * + * @param pin - PA0-PA15 + * @param mode - + * + * @return none + */ +void GPIOA_ModeCfg(uint32_t pin, GPIOModeTypeDef mode) +{ + switch(mode) + { + case GPIO_ModeIN_Floating: + R32_PA_PD_DRV &= ~pin; + R32_PA_PU &= ~pin; + R32_PA_DIR &= ~pin; + break; + + case GPIO_ModeIN_PU: + R32_PA_PD_DRV &= ~pin; + R32_PA_PU |= pin; + R32_PA_DIR &= ~pin; + break; + + case GPIO_ModeIN_PD: + R32_PA_PD_DRV |= pin; + R32_PA_PU &= ~pin; + R32_PA_DIR &= ~pin; + break; + + case GPIO_ModeOut_PP_5mA: + R32_PA_PD_DRV &= ~pin; + R32_PA_DIR |= pin; + break; + + case GPIO_ModeOut_PP_20mA: + R32_PA_PD_DRV |= pin; + R32_PA_DIR |= pin; + break; + + default: + break; + } +} + +/********************************************************************* + * @fn GPIOB_ModeCfg + * + * @brief GPIOB˿ģʽ + * + * @param pin - PB0-PB23 + * @param mode - + * + * @return none + */ +void GPIOB_ModeCfg(uint32_t pin, GPIOModeTypeDef mode) +{ + switch(mode) + { + case GPIO_ModeIN_Floating: + R32_PB_PD_DRV &= ~pin; + R32_PB_PU &= ~pin; + R32_PB_DIR &= ~pin; + break; + + case GPIO_ModeIN_PU: + R32_PB_PD_DRV &= ~pin; + R32_PB_PU |= pin; + R32_PB_DIR &= ~pin; + break; + + case GPIO_ModeIN_PD: + R32_PB_PD_DRV |= pin; + R32_PB_PU &= ~pin; + R32_PB_DIR &= ~pin; + break; + + case GPIO_ModeOut_PP_5mA: + R32_PB_PD_DRV &= ~pin; + R32_PB_DIR |= pin; + break; + + case GPIO_ModeOut_PP_20mA: + R32_PB_PD_DRV |= pin; + R32_PB_DIR |= pin; + break; + + default: + break; + } +} + +/********************************************************************* + * @fn GPIOA_ITModeCfg + * + * @brief GPIOAжģʽ + * + * @param pin - PA0-PA15 + * @param mode - + * + * @return none + */ +void GPIOA_ITModeCfg(uint32_t pin, GPIOITModeTpDef mode) +{ + switch(mode) + { + case GPIO_ITMode_LowLevel: // ͵ƽ + R16_PA_INT_MODE &= ~pin; + R32_PA_CLR |= pin; + break; + + case GPIO_ITMode_HighLevel: // ߵƽ + R16_PA_INT_MODE &= ~pin; + R32_PA_OUT |= pin; + break; + + case GPIO_ITMode_FallEdge: // ½ش + R16_PA_INT_MODE |= pin; + R32_PA_CLR |= pin; + break; + + case GPIO_ITMode_RiseEdge: // ش + R16_PA_INT_MODE |= pin; + R32_PA_OUT |= pin; + break; + + default: + break; + } + R16_PA_INT_IF = pin; + R16_PA_INT_EN |= pin; +} + +/********************************************************************* + * @fn GPIOB_ITModeCfg + * + * @brief GPIOBжģʽ + * + * @param pin - PB0-PB23 + * @param mode - + * + * @return none + */ +void GPIOB_ITModeCfg(uint32_t pin, GPIOITModeTpDef mode) +{ + uint32_t Pin = pin | ((pin & (GPIO_Pin_22 | GPIO_Pin_23)) >> 14); + switch(mode) + { + case GPIO_ITMode_LowLevel: // ͵ƽ + R16_PB_INT_MODE &= ~Pin; + R32_PB_CLR |= pin; + break; + + case GPIO_ITMode_HighLevel: // ߵƽ + R16_PB_INT_MODE &= ~Pin; + R32_PB_OUT |= pin; + break; + + case GPIO_ITMode_FallEdge: // ½ش + R16_PB_INT_MODE |= Pin; + R32_PB_CLR |= pin; + break; + + case GPIO_ITMode_RiseEdge: // ش + R16_PB_INT_MODE |= Pin; + R32_PB_OUT |= pin; + break; + + default: + break; + } + R16_PB_INT_IF = Pin; + R16_PB_INT_EN |= Pin; +} + +/********************************************************************* + * @fn GPIOPinRemap + * + * @brief 蹦ӳ + * + * @param s - Ƿʹӳ + * @param perph - RB_RF_ANT_SW_EN - RF antenna switch control output on PB16/PB17/PB18/PB19/PB20/PB21 + * RB_PIN_U0_INV - RXD0/RXD0_/TXD0/TXD0_ invert input/output + * RB_PIN_INTX - INTX: INT24/INT25 PB8/PB9 -> INT24_/INT25_ PB22/PB23 + * RB_PIN_MODEM - MODEM: PB1/PB5 -> PB14/PB15 + * RB_PIN_I2C - I2C: PB13/PB12 -> PB21/PB20 + * RB_PIN_PWMX - PWMX: PA12/PA13/PB4/PB6/PB7 -> PA6/PA7/PB1/PB2/PB3 + * RB_PIN_SPI0 - SPI0: PA12/PA13/PA14/PA15 -> PB12/PB13/PB14/PB15 + * RB_PIN_UART3 - UART3: PA4/PA5 -> PB20/PB21 + * RB_PIN_UART2 - UART2: PA6/PA7 -> PB22/PB23 + * RB_PIN_UART1 - UART1: PA8/PA9 -> PB12/PB13 + * RB_PIN_UART0 - UART0: PB4/PB7 -> PA15/PA14 + * RB_PIN_TMR3 - TMR2: PA9 -> PB23 + * RB_PIN_TMR2 - TMR2: PA11 -> PB11 + * RB_PIN_TMR1 - TMR1: PA10 -> PB10 + * RB_PIN_TMR0 - TMR0: PA9 -> PB23 + * + * @return none + */ +void GPIOPinRemap(FunctionalState s, uint16_t perph) +{ + if(s) + { + R16_PIN_ALTERNATE |= perph; + } + else + { + R16_PIN_ALTERNATE &= ~perph; + } +} + +/********************************************************************* + * @fn GPIOAGPPCfg + * + * @brief ģGPIOŹܿ + * + * @param s - ENABLE - ģ蹦ܣرֹ + * DISABLE - ֹܣرģ蹦 + * @param perph - RB_PIN_ADC8_9_IE - ADC/TKEY 9/8ͨ + * RB_PIN_ADC6_7_IE - ADC/TKEY 7/6ͨ + * RB_PIN_ADC10_IE - ADC/TKEY 10ͨ + * RB_PIN_ADC11_IE - ADC/TKEY 11 ͨ + * RB_PIN_USB2_DP_PU - USB2 U2D+ڲ + * RB_PIN_USB2_IE - USB2 + * RB_PIN_USB_DP_PU - USB UD+ڲ + * RB_PIN_USB_IE - USB + * RB_PIN_ADC0_IE - ADC/TKEY 0 ͨ + * RB_PIN_ADC1_IE - ADC/TKEY 1 ͨ + * RB_PIN_ADC12_IE - ADC/TKEY 12 ͨ + * RB_PIN_ADC13_IE - ADC/TKEY 13 ͨ + * RB_PIN_XT32K_IE - 32KHzLSE + * RB_PIN_ADC2_3_IE - ADC/TKEY 2/3 ͨ + * RB_PIN_ADC4_5_IE - ADC/TKEY 4/5 ͨ + * + * @return none + */ +void GPIOAGPPCfg(FunctionalState s, uint16_t perph) +{ + if(s) + { + R16_PIN_ANALOG_IE |= perph; + } + else + { + R16_PIN_ANALOG_IE &= ~perph; + } +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_i2c.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_i2c.c new file mode 100644 index 0000000..48d3355 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_i2c.c @@ -0,0 +1,672 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_i2c.c + * Author : WCH + * Version : V1.0 + * Date : 2021/03/15 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn I2C_Init + * + * @brief Initializes the I2Cx peripheral according to the specified + * parameters in the I2C_InitStruct. + * + * @param I2C_Mode - refer to I2C_ModeTypeDef + * @param I2C_ClockSpeed - Specifies the clock frequency(Hz). + * This parameter must be set to a value lower than 400kHz + * @param I2C_DutyCycle - Specifies the I2C fast mode duty cycle.refer to I2C_DutyTypeDef + * @param I2C_Ack - Enables or disables the acknowledgement.refer to I2C_AckTypeDef + * @param I2C_AckAddr - Specifies if 7-bit or 10-bit address is acknowledged.refer to I2C_AckAddrTypeDef + * @param I2C_OwnAddress1 - Specifies the first device own address. + * This parameter can be a 7-bit or 10-bit address. + * + * @return none + */ +void I2C_Init(I2C_ModeTypeDef I2C_Mode, UINT32 I2C_ClockSpeed, I2C_DutyTypeDef I2C_DutyCycle, + I2C_AckTypeDef I2C_Ack, I2C_AckAddrTypeDef I2C_AckAddr, UINT16 I2C_OwnAddress1) +{ + uint32_t sysClock; + uint16_t tmpreg; + + I2C_SoftwareResetCmd(ENABLE); + I2C_SoftwareResetCmd(DISABLE); + + sysClock = GetSysClock(); + + R16_I2C_CTRL2 &= ~RB_I2C_FREQ; + R16_I2C_CTRL2 |= (sysClock / 1000000); + + R16_I2C_CTRL1 &= ~RB_I2C_PE; + + if(I2C_ClockSpeed <= 100000) + { + tmpreg = (sysClock / (I2C_ClockSpeed << 1)) & RB_I2C_CCR; + + if(tmpreg < 0x04) + tmpreg = 0x04; + + R16_I2C_RTR = (((sysClock / 1000000) + 1) > 0x3F) ? 0x3F : ((sysClock / 1000000) + 1); + } + else + { + if(I2C_DutyCycle == I2C_DutyCycle_2) + { + tmpreg = (sysClock / (I2C_ClockSpeed * 3)) & RB_I2C_CCR; + } + else + { + tmpreg = (sysClock / (I2C_ClockSpeed * 25)) & RB_I2C_CCR; + tmpreg |= I2C_DutyCycle_16_9; + } + + if(tmpreg == 0) + { + tmpreg |= (uint16_t)0x0001; + } + + tmpreg |= RB_I2C_F_S; + R16_I2C_RTR = (uint16_t)((((sysClock / 1000000) * (uint16_t)300) / (uint16_t)1000) + (uint16_t)1); + } + R16_I2C_CKCFGR = tmpreg; + + R16_I2C_CTRL1 |= RB_I2C_PE; + + R16_I2C_CTRL1 &= ~(RB_I2C_SMBUS | RB_I2C_SMBTYPE | RB_I2C_ACK); + R16_I2C_CTRL1 |= I2C_Mode | I2C_Ack; + + R16_I2C_OADDR1 &= ~0xFFFF; + R16_I2C_OADDR1 |= I2C_AckAddr | I2C_OwnAddress1; +} + +/********************************************************************* + * @fn I2C_Cmd + * + * @brief Enables or disables the specified I2C peripheral. + * + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_Cmd(FunctionalState NewState) +{ + if(NewState != DISABLE) + R16_I2C_CTRL1 |= RB_I2C_PE; + else + R16_I2C_CTRL1 &= ~RB_I2C_PE; +} + +/********************************************************************* + * @fn I2C_GenerateSTART + * + * @brief Generates I2Cx communication START condition. + * + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_GenerateSTART(FunctionalState NewState) +{ + if(NewState != DISABLE) + R16_I2C_CTRL1 |= RB_I2C_START; + else + R16_I2C_CTRL1 &= ~RB_I2C_START; +} + +/********************************************************************* + * @fn I2C_GenerateSTOP + * + * @brief Generates I2Cx communication STOP condition. + * + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_GenerateSTOP(FunctionalState NewState) +{ + if(NewState != DISABLE) + R16_I2C_CTRL1 |= RB_I2C_STOP; + else + R16_I2C_CTRL1 &= ~RB_I2C_STOP; +} + +/********************************************************************* + * @fn I2C_AcknowledgeConfig + * + * @brief Enables or disables the specified I2C acknowledge feature. + * + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_AcknowledgeConfig(FunctionalState NewState) +{ + if(NewState != DISABLE) + R16_I2C_CTRL1 |= RB_I2C_ACK; + else + R16_I2C_CTRL1 &= ~RB_I2C_ACK; +} + +/********************************************************************* + * @fn I2C_OwnAddress2Config + * + * @brief Configures the specified I2C own address2. + * + * @param Address - specifies the 7bit I2C own address2. + * + * @return none + */ +void I2C_OwnAddress2Config(uint8_t Address) +{ + R16_I2C_OADDR2 &= ~RB_I2C_ADD2; + R16_I2C_OADDR2 |= (uint16_t)(Address & RB_I2C_ADD2); +} + +/********************************************************************* + * @fn I2C_DualAddressCmd + * + * @brief Enables or disables the specified I2C dual addressing mode. + * + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_DualAddressCmd(FunctionalState NewState) +{ + if(NewState != DISABLE) + R16_I2C_OADDR2 |= RB_I2C_ENDUAL; + else + R16_I2C_OADDR2 &= ~RB_I2C_ENDUAL; +} + +/********************************************************************* + * @fn I2C_GeneralCallCmd + * + * @brief Enables or disables the specified I2C general call feature. + * + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_GeneralCallCmd(FunctionalState NewState) +{ + if(NewState != DISABLE) + R16_I2C_CTRL1 |= RB_I2C_ENGC; + else + R16_I2C_CTRL1 &= ~RB_I2C_ENGC; +} + +/********************************************************************* + * @fn I2C_ITConfig + * + * @brief Enables or disables the specified I2C interrupts. + * + * @param I2C_IT - specifies the I2C interrupts sources to be enabled or disabled. + * I2C_IT_BUF - Buffer interrupt mask. + * I2C_IT_EVT - Event interrupt mask. + * I2C_IT_ERR - Error interrupt mask. + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_ITConfig(I2C_ITTypeDef I2C_IT, FunctionalState NewState) +{ + if(NewState != DISABLE) + R16_I2C_CTRL2 |= I2C_IT; + else + R16_I2C_CTRL2 &= (uint16_t)~I2C_IT; +} + +/********************************************************************* + * @fn I2C_SendData + * + * @brief Sends a data byte through the I2Cx peripheral. + * + * @param Data - Byte to be transmitted. + * + * @return none + */ +void I2C_SendData(uint8_t Data) +{ + R16_I2C_DATAR = Data; +} + +/********************************************************************* + * @fn I2C_ReceiveData + * + * @brief Returns the most recent received data by the I2Cx peripheral. + * + * @return The value of the received data. + */ +uint8_t I2C_ReceiveData(void) +{ + return (uint8_t)R16_I2C_DATAR; +} + +/********************************************************************* + * @fn I2C_Send7bitAddress + * + * @brief Transmits the address byte to select the slave device. + * + * @param Address - specifies the slave address which will be transmitted. + * @param I2C_Direction - specifies whether the I2C device will be a Transmitter or a Receiver. + * I2C_Direction_Transmitter - Transmitter mode. + * I2C_Direction_Receiver - Receiver mode. + * + * @return none + */ +void I2C_Send7bitAddress(uint8_t Address, uint8_t I2C_Direction) +{ + if(I2C_Direction != I2C_Direction_Transmitter) + Address |= OADDR1_ADD0_Set; + else + Address &= OADDR1_ADD0_Reset; + + R16_I2C_DATAR = Address; +} + +/********************************************************************* + * @fn I2C_SoftwareResetCmd + * + * @brief Enables or disables the specified I2C software reset. + * + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_SoftwareResetCmd(FunctionalState NewState) +{ + if(NewState != DISABLE) + R16_I2C_CTRL1 |= RB_I2C_SWRST; + else + R16_I2C_CTRL1 &= ~RB_I2C_SWRST; +} + +/********************************************************************* + * @fn I2C_NACKPositionConfig + * + * @brief Selects the specified I2C NACK position in master receiver mode. + * + * @param I2C_NACKPosition - specifies the NACK position. + * I2C_NACKPosition_Next - indicates that the next byte will be the last received byte. + * I2C_NACKPosition_Current - indicates that current byte is the last received byte. + * + * @return none + */ +void I2C_NACKPositionConfig(uint16_t I2C_NACKPosition) +{ + if(I2C_NACKPosition == I2C_NACKPosition_Next) + R16_I2C_CTRL1 |= I2C_NACKPosition_Next; + else + R16_I2C_CTRL1 &= I2C_NACKPosition_Current; +} + +/********************************************************************* + * @fn I2C_SMBusAlertConfig + * + * @brief Drives the SMBusAlert pin high or low for the specified I2C. + * + * @param I2C_SMBusAlert - specifies SMBAlert pin level. + * I2C_SMBusAlert_Low - SMBAlert pin driven low. + * I2C_SMBusAlert_High - SMBAlert pin driven high. + * + * @return none + */ +void I2C_SMBusAlertConfig(uint16_t I2C_SMBusAlert) +{ + if(I2C_SMBusAlert == I2C_SMBusAlert_Low) + R16_I2C_CTRL1 |= I2C_SMBusAlert_Low; + else + R16_I2C_CTRL1 &= I2C_SMBusAlert_High; +} + +/********************************************************************* + * @fn I2C_TransmitPEC + * + * @brief Enables or disables the specified I2C PEC transfer. + * + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_TransmitPEC(FunctionalState NewState) +{ + if(NewState != DISABLE) + R16_I2C_CTRL1 |= RB_I2C_PEC; + else + R16_I2C_CTRL1 &= ~RB_I2C_PEC; +} + +/********************************************************************* + * @fn I2C_PECPositionConfig + * + * @brief Selects the specified I2C PEC position. + * + * @param I2C_PECPosition - specifies the PEC position. + * I2C_PECPosition_Next - indicates that the next byte is PEC. + * I2C_PECPosition_Current - indicates that current byte is PEC. + * + * @return none + */ +void I2C_PECPositionConfig(uint16_t I2C_PECPosition) +{ + if(I2C_PECPosition == I2C_PECPosition_Next) + R16_I2C_CTRL1 |= I2C_PECPosition_Next; + else + R16_I2C_CTRL1 &= I2C_PECPosition_Current; +} + +/********************************************************************* + * @fn I2C_CalculatePEC + * + * @brief Enables or disables the PEC value calculation of the transferred bytes. + * + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_CalculatePEC(FunctionalState NewState) +{ + if(NewState != DISABLE) + R16_I2C_CTRL1 |= RB_I2C_ENPEC; + else + R16_I2C_CTRL1 &= ~RB_I2C_ENPEC; +} + +/********************************************************************* + * @fn I2C_GetPEC + * + * @brief Returns the PEC value for the specified I2C. + * + * @return The PEC value. + */ +uint8_t I2C_GetPEC(void) +{ + return (R16_I2C_STAR2 >> 8); +} + +/********************************************************************* + * @fn I2C_ARPCmd + * + * @brief Enables or disables the specified I2C ARP. + * + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_ARPCmd(FunctionalState NewState) +{ + if(NewState != DISABLE) + R16_I2C_CTRL1 |= RB_I2C_EBARP; + else + R16_I2C_CTRL1 &= ~RB_I2C_EBARP; +} + +/********************************************************************* + * @fn I2C_StretchClockCmd + * + * @brief Enables or disables the specified I2C Clock stretching. + * + * @param NewState - ENABLE or DISABLE. + * + * @return none + */ +void I2C_StretchClockCmd(FunctionalState NewState) +{ + if(NewState == DISABLE) + R16_I2C_CTRL1 |= RB_I2C_NOSTRETCH; + else + R16_I2C_CTRL1 &= ~RB_I2C_NOSTRETCH; +} + +/********************************************************************* + * @fn I2C_FastModeDutyCycleConfig + * + * @brief Selects the specified I2C fast mode duty cycle. + * + * @param I2C_DutyCycle - specifies the fast mode duty cycle. + * I2C_DutyCycle_2 - I2C fast mode Tlow/Thigh = 2. + * I2C_DutyCycle_16_9 - I2C fast mode Tlow/Thigh = 16/9. + * + * @return none + */ +void I2C_FastModeDutyCycleConfig(uint16_t I2C_DutyCycle) +{ + if(I2C_DutyCycle != I2C_DutyCycle_16_9) + R16_I2C_CKCFGR &= ~I2C_DutyCycle_16_9; + else + R16_I2C_CKCFGR |= I2C_DutyCycle_16_9; +} + +/********************************************************************* + * @fn I2C_CheckEvent + * + * @brief Checks whether the last I2Cx Event is equal to the one passed as parameter. + * + * @param I2C_EVENT - specifies the event to be checked. + * I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED : EV1. + * I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED : EV1. + * I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED : EV1. + * I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED : EV1. + * I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED : EV1. + * I2C_EVENT_SLAVE_BYTE_RECEIVED : EV2. + * (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_DUALF) : EV2. + * (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_GENCALL) : EV2. + * I2C_EVENT_SLAVE_BYTE_TRANSMITTED : EV3. + * (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_DUALF) : EV3. + * (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_GENCALL) : EV3. + * I2C_EVENT_SLAVE_ACK_FAILURE : EV3_2. + * I2C_EVENT_SLAVE_STOP_DETECTED : EV4. + * I2C_EVENT_MASTER_MODE_SELECT : EV5. + * I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED : EV6. + * I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED : EV6. + * I2C_EVENT_MASTER_BYTE_RECEIVED : EV7. + * I2C_EVENT_MASTER_BYTE_TRANSMITTING : EV8. + * I2C_EVENT_MASTER_BYTE_TRANSMITTED : EV8_2. + * I2C_EVENT_MASTER_MODE_ADDRESS10 : EV9. + * + * @return 1 - SUCCESS or 0 - ERROR. + */ +uint8_t I2C_CheckEvent(uint32_t I2C_EVENT) +{ + uint32_t lastevent = 0; + uint32_t flag1 = 0, flag2 = 0; + uint8_t status = 0; + + flag1 = R16_I2C_STAR1; + flag2 = R16_I2C_STAR2; + flag2 = flag2 << 16; + + lastevent = (flag1 | flag2) & FLAG_Mask; + + if((lastevent & I2C_EVENT) == I2C_EVENT) + { + status = !0; + } + else + { + status = 0; + } + + return status; +} + +/********************************************************************* + * @fn I2C_GetLastEvent + * + * @brief Returns the last I2Cx Event. + * + * @return The last event. + */ +uint32_t I2C_GetLastEvent(void) +{ + uint32_t lastevent = 0; + uint32_t flag1 = 0, flag2 = 0; + + flag1 = R16_I2C_STAR1; + flag2 = R16_I2C_STAR2; + flag2 = flag2 << 16; + lastevent = (flag1 | flag2) & FLAG_Mask; + + return lastevent; +} + +/********************************************************************* + * @fn I2C_GetFlagStatus + * + * @brief Checks whether the last I2Cx Event is equal to the one passed as parameter. + * + * @param I2C_FLAG - specifies the flag to check. + * I2C_FLAG_DUALF - Dual flag (Slave mode). + * I2C_FLAG_SMBHOST - SMBus host header (Slave mode). + * I2C_FLAG_SMBDEFAULT - SMBus default header (Slave mode). + * I2C_FLAG_GENCALL - General call header flag (Slave mode). + * I2C_FLAG_TRA - Transmitter/Receiver flag. + * I2C_FLAG_BUSY - Bus busy flag. + * I2C_FLAG_MSL - Master/Slave flag. + * I2C_FLAG_SMBALERT - SMBus Alert flag. + * I2C_FLAG_TIMEOUT - Timeout or Tlow error flag. + * I2C_FLAG_PECERR - PEC error in reception flag. + * I2C_FLAG_OVR - Overrun/Underrun flag (Slave mode). + * I2C_FLAG_AF - Acknowledge failure flag. + * I2C_FLAG_ARLO - Arbitration lost flag (Master mode). + * I2C_FLAG_BERR - Bus error flag. + * I2C_FLAG_TXE - Data register empty flag (Transmitter). + * I2C_FLAG_RXNE - Data register not empty (Receiver) flag. + * I2C_FLAG_STOPF - Stop detection flag (Slave mode). + * I2C_FLAG_ADD10 - 10-bit header sent flag (Master mode). + * I2C_FLAG_BTF - Byte transfer finished flag. + * I2C_FLAG_ADDR - Address sent flag (Master mode) "ADSL" + * Address matched flag (Slave mode)"ENDA". + * I2C_FLAG_SB - Start bit flag (Master mode). + * + * @return FlagStatus - SET or RESET. + */ +FlagStatus I2C_GetFlagStatus(uint32_t I2C_FLAG) +{ + FlagStatus bitstatus = RESET; + __IO uint32_t i2creg = 0, i2cxbase = 0; + + i2cxbase = (uint32_t)BA_I2C; + i2creg = I2C_FLAG >> 28; + I2C_FLAG &= FLAG_Mask; + + if(i2creg != 0) + { + i2cxbase += 0x14; + } + else + { + I2C_FLAG = (uint32_t)(I2C_FLAG >> 16); + i2cxbase += 0x18; + } + + if(((*(__IO uint32_t *)i2cxbase) & I2C_FLAG) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + + return bitstatus; +} + +/********************************************************************* + * @fn I2C_ClearFlag + * + * @brief Clears the I2Cx's pending flags. + * + * @param I2C_FLAG - specifies the flag to clear. + * I2C_FLAG_SMBALERT - SMBus Alert flag. + * I2C_FLAG_TIMEOUT - Timeout or Tlow error flag. + * I2C_FLAG_PECERR - PEC error in reception flag. + * I2C_FLAG_OVR - Overrun/Underrun flag (Slave mode). + * I2C_FLAG_AF - Acknowledge failure flag. + * I2C_FLAG_ARLO - Arbitration lost flag (Master mode). + * I2C_FLAG_BERR - Bus error flag. + * + * @return none + */ +void I2C_ClearFlag(uint32_t I2C_FLAG) +{ + uint32_t flagpos = 0; + + flagpos = I2C_FLAG & FLAG_Mask; + R16_I2C_STAR1 = (uint16_t)~flagpos; +} + +/********************************************************************* + * @fn I2C_GetITStatus + * + * @brief Checks whether the specified I2C interrupt has occurred or not. + * + * @param II2C_IT - specifies the interrupt source to check. + * I2C_FLAG_SMBALERT - SMBus Alert flag. + * I2C_FLAG_TIMEOUT - Timeout or Tlow error flag. + * I2C_FLAG_PECERR - PEC error in reception flag. + * I2C_FLAG_OVR - Overrun/Underrun flag (Slave mode). + * I2C_FLAG_AF - Acknowledge failure flag. + * I2C_FLAG_ARLO - Arbitration lost flag (Master mode). + * I2C_FLAG_BERR - Bus error flag. + * I2C_FLAG_TXE - Data register empty flag (Transmitter). + * I2C_FLAG_RXNE - Data register not empty (Receiver) flag. + * I2C_FLAG_STOPF - Stop detection flag (Slave mode). + * I2C_FLAG_ADD10 - 10-bit header sent flag (Master mode). + * I2C_FLAG_BTF - Byte transfer finished flag. + * I2C_FLAG_ADDR - Address sent flag (Master mode) "ADSL" + * Address matched flag (Slave mode)"ENDA". + * I2C_FLAG_SB - Start bit flag (Master mode). + * + * @return none + */ +ITStatus I2C_GetITStatus(uint32_t I2C_IT) +{ + ITStatus bitstatus = RESET; + uint32_t enablestatus = 0; + + enablestatus = (uint32_t)(((I2C_IT & ITEN_Mask) >> 16) & (R16_I2C_CTRL2)); + I2C_IT &= FLAG_Mask; + + if(((R16_I2C_STAR1 & I2C_IT) != (uint32_t)RESET) && enablestatus) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + + return bitstatus; +} + +/********************************************************************* + * @fn I2C_ClearITPendingBit + * + * @brief Clears the I2Cx interrupt pending bits. + * + * @param I2C_IT - specifies the interrupt pending bit to clear. + * I2C_IT_SMBALERT - SMBus Alert interrupt. + * I2C_IT_TIMEOUT - Timeout or Tlow error interrupt. + * I2C_IT_PECERR - PEC error in reception interrupt. + * I2C_IT_OVR - Overrun/Underrun interrupt (Slave mode). + * I2C_IT_AF - Acknowledge failure interrupt. + * I2C_IT_ARLO - Arbitration lost interrupt (Master mode). + * I2C_IT_BERR - Bus error interrupt. + * + * @return none + */ +void I2C_ClearITPendingBit(uint32_t I2C_IT) +{ + uint32_t flagpos = 0; + + flagpos = I2C_IT & FLAG_Mask; + R16_I2C_STAR1 = (uint16_t)~flagpos; +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_pwm.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_pwm.c new file mode 100644 index 0000000..3d885d8 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_pwm.c @@ -0,0 +1,123 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_pwm.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn PWMX_CycleCfg + * + * @brief PWM4-PWM11׼ʱ + * + * @param cyc - refer to PWMX_CycleTypeDef + * + * @return none + */ +void PWMX_CycleCfg(PWMX_CycleTypeDef cyc) +{ + switch(cyc) + { + case PWMX_Cycle_256: + R8_PWM_CONFIG = R8_PWM_CONFIG & 0xf0; + break; + + case PWMX_Cycle_255: + R8_PWM_CONFIG = (R8_PWM_CONFIG & 0xf0) | 0x01; + break; + + case PWMX_Cycle_128: + R8_PWM_CONFIG = (R8_PWM_CONFIG & 0xf0) | (1 << 2); + break; + + case PWMX_Cycle_127: + R8_PWM_CONFIG = (R8_PWM_CONFIG & 0xf0) | (1 << 2) | 0x01; + break; + + case PWMX_Cycle_64: + R8_PWM_CONFIG = (R8_PWM_CONFIG & 0xf0) | (2 << 2); + break; + + case PWMX_Cycle_63: + R8_PWM_CONFIG = (R8_PWM_CONFIG & 0xf0) | (2 << 2) | 0x01; + break; + + case PWMX_Cycle_32: + R8_PWM_CONFIG = (R8_PWM_CONFIG & 0xf0) | (3 << 2); + break; + + case PWMX_Cycle_31: + R8_PWM_CONFIG = (R8_PWM_CONFIG & 0xf0) | (3 << 2) | 0x01; + break; + + default: + break; + } +} + +/********************************************************************* + * @fn PWMX_ACTOUT + * + * @brief PWM4-PWM11ͨ + * + * @param ch - select channel of pwm, refer to channel of PWM define + * @param da - effective pulse width + * @param pr - select wave polar, refer to PWMX_PolarTypeDef + * @param s - control pwmx function, ENABLE or DISABLE + * + * @return none + */ +void PWMX_ACTOUT(uint8_t ch, uint8_t da, PWMX_PolarTypeDef pr, FunctionalState s) +{ + uint8_t i; + + if(s == DISABLE) + { + R8_PWM_OUT_EN &= ~(ch); + } + else + { + (pr) ? (R8_PWM_POLAR |= (ch)) : (R8_PWM_POLAR &= ~(ch)); + for(i = 0; i < 8; i++) + { + if((ch >> i) & 1) + { + *((volatile uint8_t *)((&R8_PWM4_DATA) + i)) = da; + } + } + R8_PWM_OUT_EN |= (ch); + } +} + +/********************************************************************* + * @fn PWMX_AlterOutCfg + * + * @brief PWM ģʽ + * + * @param ch - select group of PWM alternate output + * RB_PWM4_5_STAG_EN - PWM4 PWM5 ͨ + * RB_PWM6_7_STAG_EN - PWM6 PWM7 ͨ + * RB_PWM8_9_STAG_EN - PWM8 PWM9 ͨ + * RB_PWM10_11_STAG_EN - PWM10 PWM11 ͨ + * @param s - control pwmx function, ENABLE or DISABLE + * + * @return none + */ +void PWMX_AlterOutCfg(uint8_t ch, FunctionalState s) +{ + if(s == DISABLE) + { + R8_PWM_CONFIG &= ~(ch); + } + else + { + R8_PWM_CONFIG |= (ch); + } +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_pwr.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_pwr.c new file mode 100644 index 0000000..91f09b9 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_pwr.c @@ -0,0 +1,384 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_pwr.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn PWR_DCDCCfg + * + * @brief ڲDC/DCԴڽԼϵͳ + * + * @param s - ǷDCDCԴ + * + * @return none + */ +void PWR_DCDCCfg(FunctionalState s) +{ + uint16_t adj = R16_AUX_POWER_ADJ; + uint16_t plan = R16_POWER_PLAN; + + if(s == DISABLE) + { + + adj &= ~RB_DCDC_CHARGE; + plan &= ~(RB_PWR_DCDC_EN | RB_PWR_DCDC_PRE); // · DC/DC + sys_safe_access_enable(); + R16_AUX_POWER_ADJ = adj; + R16_POWER_PLAN = plan; + sys_safe_access_disable(); + } + else + { + adj |= RB_DCDC_CHARGE; + plan |= RB_PWR_DCDC_PRE; + sys_safe_access_enable(); + R16_AUX_POWER_ADJ = adj; + R16_POWER_PLAN = plan; + DelayUs(10); + sys_safe_access_enable(); + R16_POWER_PLAN |= RB_PWR_DCDC_EN; + sys_safe_access_disable(); + } +} + +/********************************************************************* + * @fn PWR_UnitModCfg + * + * @brief ɿصԪģĵԴ + * + * @param s - Ƿ򿪵Դ + * @param unit - please refer to unit of controllable power supply + * + * @return none + */ +void PWR_UnitModCfg(FunctionalState s, uint8_t unit) +{ + uint8_t pwr_ctrl = R8_HFCK_PWR_CTRL; + uint8_t ck32k_cfg = R8_CK32K_CONFIG; + + if(s == DISABLE) //ر + { + pwr_ctrl &= ~(unit & 0x1c); + ck32k_cfg &= ~(unit & 0x03); + } + else // + { + pwr_ctrl |= (unit & 0x1c); + ck32k_cfg |= (unit & 0x03); + } + + sys_safe_access_enable(); + R8_HFCK_PWR_CTRL = pwr_ctrl; + R8_CK32K_CONFIG = ck32k_cfg; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn PWR_PeriphClkCfg + * + * @brief ʱӿλ + * + * @param s - Ƿ򿪶Ӧʱ + * @param perph - please refer to Peripher CLK control bit define + * + * @return none + */ +void PWR_PeriphClkCfg(FunctionalState s, uint16_t perph) +{ + uint32_t sleep_ctrl = R32_SLEEP_CONTROL; + + if(s == DISABLE) + { + sleep_ctrl |= perph; + } + else + { + sleep_ctrl &= ~perph; + } + + sys_safe_access_enable(); + R32_SLEEP_CONTROL = sleep_ctrl; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn PWR_PeriphWakeUpCfg + * + * @brief ˯߻Դ + * + * @param s - Ƿ򿪴˯߻ѹ + * @param perph - ҪõĻԴ + * RB_SLP_USB_WAKE - USB ΪԴ + * RB_SLP_RTC_WAKE - RTC ΪԴ + * RB_SLP_GPIO_WAKE - GPIO ΪԴ + * RB_SLP_BAT_WAKE - BAT ΪԴ + * @param mode - refer to WakeUP_ModeypeDef + * + * @return none + */ +void PWR_PeriphWakeUpCfg(FunctionalState s, uint8_t perph, WakeUP_ModeypeDef mode) +{ + uint8_t m; + + if(s == DISABLE) + { + sys_safe_access_enable(); + R8_SLP_WAKE_CTRL &= ~perph; + } + else + { + switch(mode) + { + case Short_Delay: + m = 0x01; + break; + + case Long_Delay: + m = 0x00; + break; + + default: + m = 0x01; + break; + } + + sys_safe_access_enable(); + R8_SLP_WAKE_CTRL |= RB_WAKE_EV_MODE | perph; + sys_safe_access_enable(); + R8_SLP_POWER_CTRL &= ~(RB_WAKE_DLY_MOD); + sys_safe_access_enable(); + R8_SLP_POWER_CTRL |= m; + } + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn PowerMonitor + * + * @brief Դ + * + * @param s - Ƿ򿪴˹ + * @param vl - refer to VolM_LevelypeDef + * + * @return none + */ +void PowerMonitor(FunctionalState s, VolM_LevelypeDef vl) +{ + uint8_t ctrl = R8_BAT_DET_CTRL; + uint8_t cfg = R8_BAT_DET_CFG; + + if(s == DISABLE) + { + sys_safe_access_enable(); + R8_BAT_DET_CTRL = 0; + sys_safe_access_disable(); + } + else + { + if(vl & 0x80) + { + cfg = vl & 0x03; + ctrl = RB_BAT_MON_EN | ((vl >> 2) & 1); + } + else + { + + cfg = vl & 0x03; + ctrl = RB_BAT_DET_EN; + } + sys_safe_access_enable(); + R8_BAT_DET_CTRL = ctrl; + R8_BAT_DET_CFG = cfg; + sys_safe_access_disable(); + + mDelayuS(1); + sys_safe_access_enable(); + R8_BAT_DET_CTRL |= RB_BAT_LOW_IE | RB_BAT_LOWER_IE; + sys_safe_access_disable(); + } +} + +/********************************************************************* + * @fn LowPower_Idle + * + * @brief ͹-Idleģʽ + * + * @param none + * + * @return none + */ +__HIGH_CODE +void LowPower_Idle(void) +{ + FLASH_ROM_SW_RESET(); + R8_FLASH_CTRL = 0x04; //flashر + + PFIC->SCTLR &= ~(1 << 2); // sleep + __WFI(); + __nop(); + __nop(); +} + +/********************************************************************* + * @fn LowPower_Halt + * + * @brief ͹-Haltģʽ˵͹еHSI/5ʱУѺҪûԼѡϵͳʱԴ + * + * @param none + * + * @return none + */ +__HIGH_CODE +void LowPower_Halt(void) +{ + uint8_t x32Kpw, x32Mpw; + + FLASH_ROM_SW_RESET(); + R8_FLASH_CTRL = 0x04; //flashر + x32Kpw = R8_XT32K_TUNE; + x32Mpw = R8_XT32M_TUNE; + x32Mpw = (x32Mpw & 0xfc) | 0x03; // 150% + if(R16_RTC_CNT_32K > 0x3fff) + { // 500ms + x32Kpw = (x32Kpw & 0xfc) | 0x01; // LSE͵ + } + + sys_safe_access_enable(); + R8_BAT_DET_CTRL = 0; // رյѹ + sys_safe_access_enable(); + R8_XT32K_TUNE = x32Kpw; + R8_XT32M_TUNE = x32Mpw; + sys_safe_access_enable(); + R8_PLL_CONFIG |= (1 << 5); + sys_safe_access_disable(); + + PFIC->SCTLR |= (1 << 2); //deep sleep + __WFI(); + __nop(); + __nop(); + sys_safe_access_enable(); + R8_PLL_CONFIG &= ~(1 << 5); + sys_safe_access_disable(); +} + +/******************************************************************************* +* Function Name : LowPower_Sleep +* Description : ͹-Sleepģʽ + ע⵱ƵΪ80Mʱ˯߻жϲɵflashڴ룬˳˺ǰҪ30usӳ١ +* Input : rm: + RB_PWR_RAM2K - 2K retention SRAM + RB_PWR_RAM30K - 30K main SRAM + RB_PWR_EXTEND - USB BLE Ԫ򹩵 + RB_PWR_XROM - FlashROM + NULL - ϵԪϵ +* Return : None +*******************************************************************************/ +__HIGH_CODE +void LowPower_Sleep(uint8_t rm) +{ + uint8_t x32Kpw, x32Mpw; + uint16_t power_plan; + + x32Kpw = R8_XT32K_TUNE; + x32Mpw = R8_XT32M_TUNE; + x32Mpw = (x32Mpw & 0xfc) | 0x03; // 150% + if(R16_RTC_CNT_32K > 0x3fff) + { // 500ms + x32Kpw = (x32Kpw & 0xfc) | 0x01; // LSE͵ + } + + sys_safe_access_enable(); + R8_BAT_DET_CTRL = 0; // رյѹ + sys_safe_access_enable(); + R8_XT32K_TUNE = x32Kpw; + R8_XT32M_TUNE = x32Mpw; + sys_safe_access_disable(); + + PFIC->SCTLR |= (1 << 2); //deep sleep + + power_plan = R16_POWER_PLAN & (RB_PWR_DCDC_EN | RB_PWR_DCDC_PRE); + power_plan |= RB_PWR_PLAN_EN | RB_PWR_MUST_0010 | RB_PWR_CORE | rm; + __nop(); + sys_safe_access_enable(); + R8_SLP_POWER_CTRL |= RB_RAM_RET_LV; + R8_PLL_CONFIG |= (1 << 5); + R16_POWER_PLAN = power_plan; + + do{ + __WFI(); + __nop(); + __nop(); + DelayUs(70); + + uint8_t mac[6] = {0}; + + GetMACAddress(mac); + + if(mac[5] != 0xff) + break; + + }while(1); + + sys_safe_access_enable(); + R8_PLL_CONFIG &= ~(1 << 5); + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn LowPower_Shutdown + * + * @brief ͹-Shutdownģʽ˵͹еHSI/5ʱУѺҪûԼѡϵͳʱԴ + * @note עô˺DCDCǿƹرգѺֶٴδ + * + * @param rm - ģѡ + * RB_PWR_RAM2K - 2K retention SRAM + * RB_PWR_RAM16K - 16K main SRAM + * NULL - ϵԪϵ + * + * @return none + */ +__HIGH_CODE +void LowPower_Shutdown(uint8_t rm) +{ + uint8_t x32Kpw, x32Mpw; + + FLASH_ROM_SW_RESET(); + x32Kpw = R8_XT32K_TUNE; + x32Mpw = R8_XT32M_TUNE; + x32Mpw = (x32Mpw & 0xfc) | 0x03; // 150% + if(R16_RTC_CNT_32K > 0x3fff) + { // 500ms + x32Kpw = (x32Kpw & 0xfc) | 0x01; // LSE͵ + } + + sys_safe_access_enable(); + R8_BAT_DET_CTRL = 0; // رյѹ + sys_safe_access_enable(); + R8_XT32K_TUNE = x32Kpw; + R8_XT32M_TUNE = x32Mpw; + sys_safe_access_disable(); + SetSysClock(CLK_SOURCE_HSE_6_4MHz); + + PFIC->SCTLR |= (1 << 2); //deep sleep + + sys_safe_access_enable(); + R8_SLP_POWER_CTRL |= RB_RAM_RET_LV; + sys_safe_access_enable(); + R16_POWER_PLAN = RB_PWR_PLAN_EN | RB_PWR_MUST_0010 | rm; + __WFI(); + __nop(); + __nop(); + FLASH_ROM_SW_RESET(); + sys_safe_access_enable(); + R8_RST_WDOG_CTRL |= RB_SOFTWARE_RESET; + sys_safe_access_disable(); +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_spi0.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_spi0.c new file mode 100644 index 0000000..7b75bdf --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_spi0.c @@ -0,0 +1,368 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_SPI0.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn SPI0_MasterDefInit + * + * @brief ģʽĬϳʼģʽ0+3ȫ˫+8MHz + * + * @param none + * + * @return none + */ +void SPI0_MasterDefInit(void) +{ + R8_SPI0_CLOCK_DIV = 4; // Ƶʱ4Ƶ + R8_SPI0_CTRL_MOD = RB_SPI_ALL_CLEAR; + R8_SPI0_CTRL_MOD = RB_SPI_MOSI_OE | RB_SPI_SCK_OE; + R8_SPI0_CTRL_CFG |= RB_SPI_AUTO_IF; // BUFFER/FIFOԶIF_BYTE_END־ + R8_SPI0_CTRL_CFG &= ~RB_SPI_DMA_ENABLE; // DMAʽ +} + +/********************************************************************* + * @fn SPI0_CLKCfg + * + * @brief SPI0 ׼ʱã= d*Tsys + * + * @param c - ʱӷƵϵ + * + * @return none + */ +void SPI0_CLKCfg(uint8_t c) +{ + if(c == 2) + { + R8_SPI0_CTRL_CFG |= RB_SPI_MST_DLY_EN; + } + else + { + R8_SPI0_CTRL_CFG &= ~RB_SPI_MST_DLY_EN; + } + R8_SPI0_CLOCK_DIV = c; +} + +/********************************************************************* + * @fn SPI0_DataMode + * + * @brief ģʽ + * + * @param m - ģʽ refer to ModeBitOrderTypeDef + * + * @return none + */ +void SPI0_DataMode(ModeBitOrderTypeDef m) +{ + switch(m) + { + case Mode0_LowBitINFront: + R8_SPI0_CTRL_MOD &= ~RB_SPI_MST_SCK_MOD; + R8_SPI0_CTRL_CFG |= RB_SPI_BIT_ORDER; + break; + case Mode0_HighBitINFront: + R8_SPI0_CTRL_MOD &= ~RB_SPI_MST_SCK_MOD; + R8_SPI0_CTRL_CFG &= ~RB_SPI_BIT_ORDER; + break; + case Mode3_LowBitINFront: + R8_SPI0_CTRL_MOD |= RB_SPI_MST_SCK_MOD; + R8_SPI0_CTRL_CFG |= RB_SPI_BIT_ORDER; + break; + case Mode3_HighBitINFront: + R8_SPI0_CTRL_MOD |= RB_SPI_MST_SCK_MOD; + R8_SPI0_CTRL_CFG &= ~RB_SPI_BIT_ORDER; + break; + default: + break; + } +} + +/********************************************************************* + * @fn SPI0_MasterSendByte + * + * @brief ͵ֽ (buffer) + * + * @param d - ֽ + * + * @return none + */ +void SPI0_MasterSendByte(uint8_t d) +{ + R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; + R8_SPI0_BUFFER = d; + while(!(R8_SPI0_INT_FLAG & RB_SPI_FREE)); +} + +/********************************************************************* + * @fn SPI0_MasterRecvByte + * + * @brief յֽ (buffer) + * + * @param none + * + * @return յֽ + */ +uint8_t SPI0_MasterRecvByte(void) +{ + R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; + R8_SPI0_BUFFER = 0xFF; // + while(!(R8_SPI0_INT_FLAG & RB_SPI_FREE)); + return (R8_SPI0_BUFFER); +} + +/********************************************************************* + * @fn SPI0_MasterTrans + * + * @brief ʹFIFOͶֽ + * + * @param pbuf - ͵׵ַ + * @param len - ͵ݳȣ4095 + * + * @return none + */ +void SPI0_MasterTrans(uint8_t *pbuf, uint16_t len) +{ + uint16_t sendlen; + + sendlen = len; + R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; // ݷΪ + R16_SPI0_TOTAL_CNT = sendlen; // Ҫ͵ݳ + R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END; + while(sendlen) + { + if(R8_SPI0_FIFO_COUNT < SPI_FIFO_SIZE) + { + R8_SPI0_FIFO = *pbuf; + pbuf++; + sendlen--; + } + } + while(R8_SPI0_FIFO_COUNT != 0); // ȴFIFOеȫ +} + +/********************************************************************* + * @fn SPI0_MasterRecv + * + * @brief ʹFIFOնֽ + * + * @param pbuf - յ׵ַ + * @param len - յݳȣ4095 + * + * @return none + */ +void SPI0_MasterRecv(uint8_t *pbuf, uint16_t len) +{ + uint16_t readlen; + + readlen = len; + R8_SPI0_CTRL_MOD |= RB_SPI_FIFO_DIR; // ݷΪ + R16_SPI0_TOTAL_CNT = len; // ҪյݳȣFIFOΪ볤ȲΪ0 */ + R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END; + while(readlen) + { + if(R8_SPI0_FIFO_COUNT) + { + *pbuf = R8_SPI0_FIFO; + pbuf++; + readlen--; + } + } +} + +/********************************************************************* + * @fn SPI0_MasterDMATrans + * + * @brief DMAʽ + * + * @param pbuf - ʼַ,Ҫֽڶ + * @param len - ݳ + * + * @return none + */ +void SPI0_MasterDMATrans(uint8_t *pbuf, uint16_t len) +{ + R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; + R16_SPI0_DMA_BEG = (uint32_t)pbuf; + R16_SPI0_DMA_END = (uint32_t)(pbuf + len); + R16_SPI0_TOTAL_CNT = len; + R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END | RB_SPI_IF_DMA_END; + R8_SPI0_CTRL_CFG |= RB_SPI_DMA_ENABLE; + while(!(R8_SPI0_INT_FLAG & RB_SPI_IF_CNT_END)); + R8_SPI0_CTRL_CFG &= ~RB_SPI_DMA_ENABLE; +} + +/********************************************************************* + * @fn SPI0_MasterDMARecv + * + * @brief DMAʽ + * + * @param pbuf - ݴʼַ,Ҫֽڶ + * @param len - ݳ + * + * @return none + */ +void SPI0_MasterDMARecv(uint8_t *pbuf, uint16_t len) +{ + R8_SPI0_CTRL_MOD |= RB_SPI_FIFO_DIR; + R16_SPI0_DMA_BEG = (uint32_t)pbuf; + R16_SPI0_DMA_END = (uint32_t)(pbuf + len); + R16_SPI0_TOTAL_CNT = len; + R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END | RB_SPI_IF_DMA_END; + R8_SPI0_CTRL_CFG |= RB_SPI_DMA_ENABLE; + while(!(R8_SPI0_INT_FLAG & RB_SPI_IF_CNT_END)); + R8_SPI0_CTRL_CFG &= ~RB_SPI_DMA_ENABLE; +} + +/********************************************************************* + * @fn SPI0_SlaveInit + * + * @brief 豸ģʽĬϳʼMISOGPIOӦΪģʽ + * + * @return none + */ +void SPI0_SlaveInit(void) +{ + R8_SPI0_CTRL_MOD = RB_SPI_ALL_CLEAR; + R8_SPI0_CTRL_MOD = RB_SPI_MISO_OE | RB_SPI_MODE_SLAVE; + R8_SPI0_CTRL_CFG |= RB_SPI_AUTO_IF; +} + +/********************************************************************* + * @fn SPI0_SlaveRecvByte + * + * @brief ӻģʽһֽ + * + * @return յ + */ +uint8_t SPI0_SlaveRecvByte(void) +{ + R8_SPI0_CTRL_MOD |= RB_SPI_FIFO_DIR; + while(R8_SPI0_FIFO_COUNT == 0); + return R8_SPI0_FIFO; +} + +/********************************************************************* + * @fn SPI0_SlaveSendByte + * + * @brief ӻģʽһֽ + * + * @param d - + * + * @return none + */ +void SPI0_SlaveSendByte(uint8_t d) +{ + R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; + R8_SPI0_FIFO = d; + while(R8_SPI0_FIFO_COUNT != 0); // ȴ +} + +/********************************************************************* + * @fn SPI0_SlaveRecv + * + * @brief ӻģʽնֽ + * + * @param pbuf - ݴʼַ + * @param len - ݳ + * + * @return none + */ +void SPI0_SlaveRecv(uint8_t *pbuf, uint16_t len) +{ + uint16_t revlen; + + revlen = len; + R8_SPI0_CTRL_MOD |= RB_SPI_FIFO_DIR; + R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END; + while(revlen) + { + if(R8_SPI0_FIFO_COUNT) + { + *pbuf = R8_SPI0_FIFO; + pbuf++; + revlen--; + } + } +} + +/********************************************************************* + * @fn SPI0_SlaveTrans + * + * @brief ӻģʽͶֽ + * + * @param pbuf - ͵׵ַ + * @param len - ͵ݳȣ4095 + * + * @return none + */ +void SPI0_SlaveTrans(uint8_t *pbuf, uint16_t len) +{ + uint16_t sendlen; + + sendlen = len; + R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; // ݷΪ + R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END; + while(sendlen) + { + if(R8_SPI0_FIFO_COUNT < SPI_FIFO_SIZE) + { + R8_SPI0_FIFO = *pbuf; + pbuf++; + sendlen--; + } + } + while(R8_SPI0_FIFO_COUNT != 0); // ȴFIFOеȫ +} + +/********************************************************************* + * @fn SPI0_SlaveDMARecv + * + * @brief DMAʽ + * + * @param pbuf - ݴʼַ,Ҫֽڶ + * @param len - ݳ + * + * @return none + */ +void SPI0_SlaveDMARecv(uint8_t *pbuf, uint16_t len) +{ + R8_SPI0_CTRL_MOD |= RB_SPI_FIFO_DIR; + R16_SPI0_DMA_BEG = (uint32_t)pbuf; + R16_SPI0_DMA_END = (uint32_t)(pbuf + len); + R16_SPI0_TOTAL_CNT = len; + R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END | RB_SPI_IF_DMA_END; + R8_SPI0_CTRL_CFG |= RB_SPI_DMA_ENABLE; + while(!(R8_SPI0_INT_FLAG & RB_SPI_IF_CNT_END)); + R8_SPI0_CTRL_CFG &= ~RB_SPI_DMA_ENABLE; +} + +/********************************************************************* + * @fn SPI0_SlaveDMATrans + * + * @brief DMAʽ + * + * @param pbuf - ʼַ,Ҫֽڶ + * @param len - ݳ + * + * @return none + */ +void SPI0_SlaveDMATrans(uint8_t *pbuf, uint16_t len) +{ + R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; + R16_SPI0_DMA_BEG = (uint32_t)pbuf; + R16_SPI0_DMA_END = (uint32_t)(pbuf + len); + R16_SPI0_TOTAL_CNT = len; + R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END | RB_SPI_IF_DMA_END; + R8_SPI0_CTRL_CFG |= RB_SPI_DMA_ENABLE; + while(!(R8_SPI0_INT_FLAG & RB_SPI_IF_CNT_END)); + R8_SPI0_CTRL_CFG &= ~RB_SPI_DMA_ENABLE; +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_spi1.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_spi1.c new file mode 100644 index 0000000..e96b9e0 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_spi1.c @@ -0,0 +1,178 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_SPI1.c + * Author : WCH + * Version : V1.0 + * Date : 2018/12/15 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn SPI1_MasterDefInit + * + * @brief ģʽĬϳʼģʽ0+3ȫ˫+8MHz + * + * @param none + * + * @return none + */ +void SPI1_MasterDefInit(void) +{ + R8_SPI1_CLOCK_DIV = 4; // Ƶʱ4Ƶ + R8_SPI1_CTRL_MOD = RB_SPI_ALL_CLEAR; + R8_SPI1_CTRL_MOD = RB_SPI1_SDO_OE | RB_SPI_SCK_OE; + R8_SPI1_CTRL_CFG |= RB_SPI_AUTO_IF; // BUFFER/FIFOԶIF_BYTE_END־ +} + +/********************************************************************* + * @fn SPI1_CLKCfg + * + * @brief SPI1 ׼ʱã= d*Tsys + * + * @param c - ʱӷƵϵ + * + * @return none + */ +void SPI1_CLKCfg(uint8_t c) +{ + if(c == 2) + { + R8_SPI1_CTRL_CFG |= RB_SPI_MST_DLY_EN; + } + else + { + R8_SPI1_CTRL_CFG &= ~RB_SPI_MST_DLY_EN; + } + R8_SPI1_CLOCK_DIV = c; +} + +/********************************************************************* + * @fn SPI1_DataMode + * + * @brief ģʽ + * + * @param m - ģʽ refer to ModeBitOrderTypeDef + * + * @return none + */ +void SPI1_DataMode(ModeBitOrderTypeDef m) +{ + switch(m) + { + case Mode0_LowBitINFront: + R8_SPI1_CTRL_MOD &= ~RB_SPI_MST_SCK_MOD; + R8_SPI1_CTRL_CFG |= RB_SPI_BIT_ORDER; + break; + case Mode0_HighBitINFront: + R8_SPI1_CTRL_MOD &= ~RB_SPI_MST_SCK_MOD; + R8_SPI1_CTRL_CFG &= ~RB_SPI_BIT_ORDER; + break; + case Mode3_LowBitINFront: + R8_SPI1_CTRL_MOD |= RB_SPI_MST_SCK_MOD; + R8_SPI1_CTRL_CFG |= RB_SPI_BIT_ORDER; + break; + case Mode3_HighBitINFront: + R8_SPI1_CTRL_MOD |= RB_SPI_MST_SCK_MOD; + R8_SPI1_CTRL_CFG &= ~RB_SPI_BIT_ORDER; + break; + default: + break; + } +} + +/********************************************************************* + * @fn SPI1_MasterSendByte + * + * @brief ͵ֽ (buffer) + * + * @param d - ֽ + * + * @return none + */ +void SPI1_MasterSendByte(uint8_t d) +{ + R8_SPI1_CTRL_MOD &= ~RB_SPI_FIFO_DIR; + R8_SPI1_BUFFER = d; + while(!(R8_SPI1_INT_FLAG & RB_SPI_FREE)); +} + +/********************************************************************* + * @fn SPI1_MasterRecvByte + * + * @brief յֽ (buffer) + * + * @param none + * + * @return յֽ + */ +uint8_t SPI1_MasterRecvByte(void) +{ + R8_SPI1_CTRL_MOD &= ~RB_SPI_FIFO_DIR; + R8_SPI1_BUFFER = 0xFF; // + while(!(R8_SPI1_INT_FLAG & RB_SPI_FREE)); + return (R8_SPI1_BUFFER); +} + +/********************************************************************* + * @fn SPI1_MasterTrans + * + * @brief ʹFIFOͶֽ + * + * @param pbuf - ͵׵ַ + * @param len - ͵ݳȣ4095 + * + * @return none + */ +void SPI1_MasterTrans(uint8_t *pbuf, uint16_t len) +{ + uint16_t sendlen; + + sendlen = len; + R8_SPI1_CTRL_MOD &= ~RB_SPI_FIFO_DIR; // ݷΪ + R16_SPI1_TOTAL_CNT = sendlen; // Ҫ͵ݳ + R8_SPI1_INT_FLAG = RB_SPI_IF_CNT_END; + while(sendlen) + { + if(R8_SPI1_FIFO_COUNT < SPI_FIFO_SIZE) + { + R8_SPI1_FIFO = *pbuf; + pbuf++; + sendlen--; + } + } + while(R8_SPI1_FIFO_COUNT != 0); // ȴFIFOеȫ +} + +/********************************************************************* + * @fn SPI1_MasterRecv + * + * @brief ʹFIFOնֽ + * + * @param pbuf - յ׵ַ + * @param len - յݳȣ4095 + * + * @return none + */ +void SPI1_MasterRecv(uint8_t *pbuf, uint16_t len) +{ + uint16_t readlen; + + readlen = len; + R8_SPI1_CTRL_MOD |= RB_SPI_FIFO_DIR; // ݷΪ + R16_SPI1_TOTAL_CNT = len; // ҪյݳȣFIFOΪ볤ȲΪ0 */ + R8_SPI1_INT_FLAG = RB_SPI_IF_CNT_END; + while(readlen) + { + if(R8_SPI1_FIFO_COUNT) + { + *pbuf = R8_SPI1_FIFO; + pbuf++; + readlen--; + } + } +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_sys.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_sys.c new file mode 100644 index 0000000..9ec9509 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_sys.c @@ -0,0 +1,371 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_SYS.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn SetSysClock + * + * @brief ϵͳʱ + * + * @param sc - ϵͳʱԴѡ refer to SYS_CLKTypeDef + * + * @return none + */ +__HIGH_CODE +void SetSysClock(SYS_CLKTypeDef sc) +{ + uint32_t i; + sys_safe_access_enable(); + R8_PLL_CONFIG &= ~(1 << 5); // + sys_safe_access_disable(); + if(sc & 0x20) + { // HSE div + if(!(R8_HFCK_PWR_CTRL & RB_CLK_XT32M_PON)) + { + sys_safe_access_enable(); + R8_HFCK_PWR_CTRL |= RB_CLK_XT32M_PON; // HSE power on + for(i = 0; i < 1200; i++) + { + __nop(); + __nop(); + } + } + + sys_safe_access_enable(); + R16_CLK_SYS_CFG = (0 << 6) | (sc & 0x1f); + __nop(); + __nop(); + __nop(); + __nop(); + sys_safe_access_disable(); + sys_safe_access_enable(); + SAFEOPERATE; + R8_FLASH_CFG = 0X51; + sys_safe_access_disable(); + } + + else if(sc & 0x40) + { // PLL div + if(!(R8_HFCK_PWR_CTRL & RB_CLK_PLL_PON)) + { + sys_safe_access_enable(); + R8_HFCK_PWR_CTRL |= RB_CLK_PLL_PON; // PLL power on + for(i = 0; i < 2000; i++) + { + __nop(); + __nop(); + } + } + sys_safe_access_enable(); + R16_CLK_SYS_CFG = (1 << 6) | (sc & 0x1f); + __nop(); + __nop(); + __nop(); + __nop(); + sys_safe_access_disable(); + if(sc == CLK_SOURCE_PLL_80MHz) + { + sys_safe_access_enable(); + R8_FLASH_CFG = 0X02; + sys_safe_access_disable(); + } + else + { + sys_safe_access_enable(); + R8_FLASH_CFG = 0X52; + sys_safe_access_disable(); + } + } + else + { + sys_safe_access_enable(); + R16_CLK_SYS_CFG |= RB_CLK_SYS_MOD; + } + //FLASH clk + sys_safe_access_enable(); + R8_PLL_CONFIG |= 1 << 7; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn GetSysClock + * + * @brief ȡǰϵͳʱ + * + * @param none + * + * @return Hz + */ +uint32_t GetSysClock(void) +{ + uint16_t rev; + + rev = R16_CLK_SYS_CFG & 0xff; + if((rev & 0x40) == (0 << 6)) + { // 32MзƵ + return (32000000 / (rev & 0x1f)); + } + else if((rev & RB_CLK_SYS_MOD) == (1 << 6)) + { // PLLзƵ + return (480000000 / (rev & 0x1f)); + } + else + { // 32KƵ + return (32000); + } +} + +/********************************************************************* + * @fn SYS_GetInfoSta + * + * @brief ȡǰϵͳϢ״̬ + * + * @param i - refer to SYS_InfoStaTypeDef + * + * @return Ƿ + */ +uint8_t SYS_GetInfoSta(SYS_InfoStaTypeDef i) +{ + if(i == STA_SAFEACC_ACT) + { + return (R8_SAFE_ACCESS_SIG & RB_SAFE_ACC_ACT); + } + else + { + return (R8_GLOB_CFG_INFO & (1 << i)); + } +} + +/********************************************************************* + * @fn SYS_ResetExecute + * + * @brief ִϵͳλ + * + * @param none + * + * @return none + */ +__HIGH_CODE +void SYS_ResetExecute(void) +{ + FLASH_ROM_SW_RESET(); + sys_safe_access_enable(); + R8_RST_WDOG_CTRL |= RB_SOFTWARE_RESET; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn SYS_DisableAllIrq + * + * @brief رжϣǰжֵ + * + * @param pirqv - ǰжֵ + * + * @return none + */ +void SYS_DisableAllIrq(uint32_t *pirqv) +{ + *pirqv = (PFIC->ISR[0] >> 8) | (PFIC->ISR[1] << 24); + PFIC->IRER[0] = 0xffffffff; + PFIC->IRER[1] = 0xffffffff; +} + +/********************************************************************* + * @fn SYS_RecoverIrq + * + * @brief ָ֮ǰرյжֵ + * + * @param irq_status - ǰжֵ + * + * @return none + */ +void SYS_RecoverIrq(uint32_t irq_status) +{ + PFIC->IENR[0] = (irq_status << 8); + PFIC->IENR[1] = (irq_status >> 24); +} + +/********************************************************************* + * @fn SYS_GetSysTickCnt + * + * @brief ȡǰϵͳ(SYSTICK)ֵ + * + * @param none + * + * @return ǰֵ + */ +uint32_t SYS_GetSysTickCnt(void) +{ + uint32_t val; + + val = SysTick->CNT; + return (val); +} + +/********************************************************************* + * @fn WWDG_ITCfg + * + * @brief Źʱжʹ + * + * @param s - Ƿж + * + * @return none + */ +void WWDG_ITCfg(FunctionalState s) +{ + uint8_t ctrl = R8_RST_WDOG_CTRL; + + if(s == DISABLE) + { + ctrl &= ~RB_WDOG_INT_EN; + } + else + { + ctrl |= RB_WDOG_INT_EN; + } + + sys_safe_access_enable(); + R8_RST_WDOG_CTRL = ctrl; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn WWDG_ResetCfg + * + * @brief Źʱλ + * + * @param s - Ƿλ + * + * @return none + */ +void WWDG_ResetCfg(FunctionalState s) +{ + uint8_t ctrl = R8_RST_WDOG_CTRL; + + if(s == DISABLE) + { + ctrl &= ~RB_WDOG_RST_EN; + } + else + { + ctrl |= RB_WDOG_RST_EN; + } + + sys_safe_access_enable(); + R8_RST_WDOG_CTRL = ctrl; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn WWDG_ClearFlag + * + * @brief Źжϱ־¼ؼֵҲ + * + * @param none + * + * @return none + */ +void WWDG_ClearFlag(void) +{ + sys_safe_access_enable(); + R8_RST_WDOG_CTRL |= RB_WDOG_INT_FLAG; + sys_safe_access_disable(); +} + +/********************************************************************* + * @fn HardFault_Handler + * + * @brief ӲжϣִиλλΪϵ縴λ + * + * @param none + * + * @return none + */ +__INTERRUPT +__HIGH_CODE +__attribute__((weak)) +void HardFault_Handler(void) +{ + FLASH_ROM_SW_RESET(); + sys_safe_access_enable(); + R16_INT32K_TUNE = 0xFFFF; + sys_safe_access_enable(); + R8_RST_WDOG_CTRL |= RB_SOFTWARE_RESET; + sys_safe_access_disable(); + while(1); +} + +/********************************************************************* + * @fn mDelayuS + * + * @brief uS ʱ + * + * @param t - ʱ + * + * @return none + */ +__HIGH_CODE +void mDelayuS(uint16_t t) +{ + uint32_t i; +#if(FREQ_SYS == 80000000) + i = t * 20; +#elif(FREQ_SYS == 60000000) + i = t * 15; +#elif(FREQ_SYS == 48000000) + i = t * 12; +#elif(FREQ_SYS == 40000000) + i = t * 10; +#elif(FREQ_SYS == 32000000) + i = t << 3; +#elif(FREQ_SYS == 24000000) + i = t * 6; +#elif(FREQ_SYS == 16000000) + i = t << 2; +#elif(FREQ_SYS == 8000000) + i = t << 1; +#elif(FREQ_SYS == 4000000) + i = t; +#elif(FREQ_SYS == 2000000) + i = t >> 1; +#elif(FREQ_SYS == 1000000) + i = t >> 2; +#else + i = t << 1; +#endif + do + { + __nop(); + } while(--i); +} + +/********************************************************************* + * @fn mDelaymS + * + * @brief mS ʱ + * + * @param t - ʱ + * + * @return none + */ +__HIGH_CODE +void mDelaymS(uint16_t t) +{ + uint16_t i; + + for(i = 0; i < t; i++) + { + mDelayuS(1000); + } +} + diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer0.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer0.c new file mode 100644 index 0000000..07c3fb9 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer0.c @@ -0,0 +1,75 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_timer0.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn TMR0_TimerInit + * + * @brief ʱܳʼ + * + * @param t - ʱʱ䣬ڵǰϵͳʱTsys, ʱ 67108864 + * + * @return none + */ +void TMR0_TimerInit(uint32_t t) +{ + R32_TMR0_CNT_END = t; + R8_TMR0_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR0_CTRL_MOD = RB_TMR_COUNT_EN; +} + +/********************************************************************* + * @fn TMR0_EXTSingleCounterInit + * + * @brief ؼܳʼ + * + * @param cap - ɼ + * + * @return none + */ +void TMR0_EXTSingleCounterInit(CapModeTypeDef cap) +{ + R8_TMR0_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR0_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_CAP_COUNT | RB_TMR_MODE_IN | (cap << 6); +} + +/********************************************************************* + * @fn TMR0_PWMInit + * + * @brief PWM ʼ + * + * @param pr - select wave polar, refer to PWMX_PolarTypeDef + * @param ts - set pwm repeat times, refer to PWM_RepeatTsTypeDef + * + * @return none + */ +void TMR0_PWMInit(PWMX_PolarTypeDef pr, PWM_RepeatTsTypeDef ts) +{ + // R8_TMR0_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR0_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_OUT_EN | (pr << 4) | (ts << 6); +} + +/********************************************************************* + * @fn TMR0_CapInit + * + * @brief ⲿźŲ׽ܳʼ + * + * @param cap - select capture mode, refer to CapModeTypeDef + * + * @return none + */ +void TMR0_CapInit(CapModeTypeDef cap) +{ + R8_TMR0_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR0_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_MODE_IN | (cap << 6); +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer1.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer1.c new file mode 100644 index 0000000..fff4d79 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer1.c @@ -0,0 +1,104 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_timer1.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn TMR1_TimerInit + * + * @brief ʱܳʼ + * + * @param t - ʱʱ䣬ڵǰϵͳʱTsys, ʱ 67108864 + * + * @return none + */ +void TMR1_TimerInit(uint32_t t) +{ + R32_TMR1_CNT_END = t; + R8_TMR1_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR1_CTRL_MOD = RB_TMR_COUNT_EN; +} + +/********************************************************************* + * @fn TMR1_EXTSingleCounterInit + * + * @brief ؼܳʼ + * + * @param cap - ɼ + * + * @return none + */ +void TMR1_EXTSingleCounterInit(CapModeTypeDef cap) +{ + R8_TMR1_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR1_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_CAP_COUNT | RB_TMR_MODE_IN | (cap << 6); +} + +/********************************************************************* + * @fn TMR1_PWMInit + * + * @brief PWM ʼ + * + * @param pr - select wave polar, refer to PWMX_PolarTypeDef + * @param ts - set pwm repeat times, refer to PWM_RepeatTsTypeDef + * + * @return none + */ +void TMR1_PWMInit(PWMX_PolarTypeDef pr, PWM_RepeatTsTypeDef ts) +{ + // R8_TMR1_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR1_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_OUT_EN | (pr << 4) | (ts << 6); +} + +/********************************************************************* + * @fn TMR1_CapInit + * + * @brief ⲿźŲ׽ܳʼ + * + * @param cap - select capture mode, refer to CapModeTypeDef + * + * @return none + */ +void TMR1_CapInit(CapModeTypeDef cap) +{ + R8_TMR1_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR1_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_MODE_IN | (cap << 6); +} + +/********************************************************************* + * @fn TMR1_DMACfg + * + * @brief DMA + * + * @param s - ǷDMA + * @param startAddr - DMA ʼַ + * @param endAddr - DMA ַ + * @param m - DMAģʽ + * + * @return none + */ +void TMR1_DMACfg(uint8_t s, uint16_t startAddr, uint16_t endAddr, DMAModeTypeDef m) +{ + if(s == DISABLE) + { + R8_TMR1_CTRL_DMA = 0; + } + else + { + R16_TMR1_DMA_BEG = startAddr; + R16_TMR1_DMA_END = endAddr; + if(m) + R8_TMR1_CTRL_DMA = RB_TMR_DMA_LOOP | RB_TMR_DMA_ENABLE; + else + R8_TMR1_CTRL_DMA = RB_TMR_DMA_ENABLE; + } +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer2.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer2.c new file mode 100644 index 0000000..3ef20d1 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer2.c @@ -0,0 +1,104 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_timer2.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn TMR2_TimerInit + * + * @brief ʱܳʼ + * + * @param t - ʱʱ䣬ڵǰϵͳʱTsys, ʱ 67108864 + * + * @return none + */ +void TMR2_TimerInit(uint32_t t) +{ + R32_TMR2_CNT_END = t; + R8_TMR2_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR2_CTRL_MOD = RB_TMR_COUNT_EN; +} + +/********************************************************************* + * @fn TMR2_EXTSingleCounterInit + * + * @brief ؼܳʼ + * + * @param cap - ɼ + * + * @return none + */ +void TMR2_EXTSingleCounterInit(CapModeTypeDef cap) +{ + R8_TMR2_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR2_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_CAP_COUNT | RB_TMR_MODE_IN | (cap << 6); +} + +/********************************************************************* + * @fn TMR2_PWMInit + * + * @brief PWM ʼ + * + * @param pr - select wave polar, refer to PWMX_PolarTypeDef + * @param ts - set pwm repeat times, refer to PWM_RepeatTsTypeDef + * + * @return none + */ +void TMR2_PWMInit(PWMX_PolarTypeDef pr, PWM_RepeatTsTypeDef ts) +{ + // R8_TMR2_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR2_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_OUT_EN | (pr << 4) | (ts << 6); +} + +/********************************************************************* + * @fn TMR2_CapInit + * + * @brief ⲿźŲ׽ܳʼ + * + * @param cap - select capture mode, refer to CapModeTypeDef + * + * @return none + */ +void TMR2_CapInit(CapModeTypeDef cap) +{ + R8_TMR2_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR2_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_MODE_IN | (cap << 6); +} + +/********************************************************************* + * @fn TMR2_DMACfg + * + * @brief DMA + * + * @param s - ǷDMA + * @param startAddr - DMA ʼַ + * @param endAddr - DMA ַ + * @param m - DMAģʽ + * + * @return none + */ +void TMR2_DMACfg(uint8_t s, uint16_t startAddr, uint16_t endAddr, DMAModeTypeDef m) +{ + if(s == DISABLE) + { + R8_TMR2_CTRL_DMA = 0; + } + else + { + R16_TMR2_DMA_BEG = startAddr; + R16_TMR2_DMA_END = endAddr; + if(m) + R8_TMR2_CTRL_DMA = RB_TMR_DMA_LOOP | RB_TMR_DMA_ENABLE; + else + R8_TMR2_CTRL_DMA = RB_TMR_DMA_ENABLE; + } +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer3.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer3.c new file mode 100644 index 0000000..68c9140 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_timer3.c @@ -0,0 +1,75 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_timer3.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn TMR3_TimerInit + * + * @brief ʱܳʼ + * + * @param t - ʱʱ䣬ڵǰϵͳʱTsys, ʱ 67108864 + * + * @return none + */ +void TMR3_TimerInit(uint32_t t) +{ + R32_TMR3_CNT_END = t; + R8_TMR3_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR3_CTRL_MOD = RB_TMR_COUNT_EN; +} + +/********************************************************************* + * @fn TMR3_EXTSingleCounterInit + * + * @brief ؼܳʼ + * + * @param cap - ɼ + * + * @return none + */ +void TMR3_EXTSingleCounterInit(CapModeTypeDef cap) +{ + R8_TMR3_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR3_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_CAP_COUNT | RB_TMR_MODE_IN | (cap << 6); +} + +/********************************************************************* + * @fn TMR3_PWMInit + * + * @brief PWM ʼ + * + * @param pr - select wave polar, refer to PWMX_PolarTypeDef + * @param ts - set pwm repeat times, refer to PWM_RepeatTsTypeDef + * + * @return none + */ +void TMR3_PWMInit(PWMX_PolarTypeDef pr, PWM_RepeatTsTypeDef ts) +{ + // R8_TMR3_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR3_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_OUT_EN | (pr << 4) | (ts << 6); +} + +/********************************************************************* + * @fn TMR3_CapInit + * + * @brief ⲿźŲ׽ܳʼ + * + * @param cap - select capture mode, refer to CapModeTypeDef + * + * @return none + */ +void TMR3_CapInit(CapModeTypeDef cap) +{ + R8_TMR3_CTRL_MOD = RB_TMR_ALL_CLEAR; + R8_TMR3_CTRL_MOD = RB_TMR_COUNT_EN | RB_TMR_MODE_IN | (cap << 6); +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart0.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart0.c new file mode 100644 index 0000000..546da00 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart0.c @@ -0,0 +1,151 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_uart0.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn UART0_DefInit + * + * @brief Ĭϳʼ + * + * @param none + * + * @return none + */ +void UART0_DefInit(void) +{ + UART0_BaudRateCfg(115200); + R8_UART0_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN; // FIFO򿪣4ֽ + R8_UART0_LCR = RB_LCR_WORD_SZ; + R8_UART0_IER = RB_IER_TXD_EN; + R8_UART0_DIV = 1; +} + +/********************************************************************* + * @fn UART0_BaudRateCfg + * + * @brief ڲ + * + * @param baudrate - + * + * @return none + */ +void UART0_BaudRateCfg(uint32_t baudrate) +{ + uint32_t x; + + x = 10 * GetSysClock() / 8 / baudrate; + x = (x + 5) / 10; + R16_UART0_DL = (uint16_t)x; +} + +/********************************************************************* + * @fn UART0_ByteTrigCfg + * + * @brief ֽڴж + * + * @param b - ֽ refer to UARTByteTRIGTypeDef + * + * @return none + */ +void UART0_ByteTrigCfg(UARTByteTRIGTypeDef b) +{ + R8_UART0_FCR = (R8_UART0_FCR & ~RB_FCR_FIFO_TRIG) | (b << 6); +} + +/********************************************************************* + * @fn UART0_INTCfg + * + * @brief ж + * + * @param s - жϿ״̬ǷʹӦж + * @param i - ж + * RB_IER_MODEM_CHG - ƽ״̬仯жʹλ UART0 ֧֣ + * RB_IER_LINE_STAT - ·״̬ж + * RB_IER_THR_EMPTY - ͱּĴж + * RB_IER_RECV_RDY - ж + * + * @return none + */ +void UART0_INTCfg(FunctionalState s, uint8_t i) +{ + if(s) + { + R8_UART0_IER |= i; + R8_UART0_MCR |= RB_MCR_INT_OE; + } + else + { + R8_UART0_IER &= ~i; + } +} + +/********************************************************************* + * @fn UART0_Reset + * + * @brief λ + * + * @param none + * + * @return none + */ +void UART0_Reset(void) +{ + R8_UART0_IER = RB_IER_RESET; +} + +/********************************************************************* + * @fn UART0_SendString + * + * @brief ڶֽڷ + * + * @param buf - ͵׵ַ + * @param l - ͵ݳ + * + * @return none + */ +void UART0_SendString(uint8_t *buf, uint16_t l) +{ + uint16_t len = l; + + while(len) + { + if(R8_UART0_TFC != UART_FIFO_SIZE) + { + R8_UART0_THR = *buf++; + len--; + } + } +} + +/********************************************************************* + * @fn UART0_RecvString + * + * @brief ڶȡֽ + * + * @param buf - ȡݴŻ׵ַ + * + * @return ȡݳ + */ +uint16_t UART0_RecvString(uint8_t *buf) +{ + uint16_t len = 0; + + while(R8_UART0_RFC) + { + *buf++ = R8_UART0_RBR; + len++; + } + + return (len); +} + diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart1.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart1.c new file mode 100644 index 0000000..da7a22d --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart1.c @@ -0,0 +1,150 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_uart1.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn UART1_DefInit + * + * @brief Ĭϳʼ + * + * @param none + * + * @return none + */ +void UART1_DefInit(void) +{ + UART1_BaudRateCfg(115200); + R8_UART1_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN; // FIFO򿪣4ֽ + R8_UART1_LCR = RB_LCR_WORD_SZ; + R8_UART1_IER = RB_IER_TXD_EN; + R8_UART1_DIV = 1; +} + +/********************************************************************* + * @fn UART1_BaudRateCfg + * + * @brief ڲ + * + * @param baudrate - + * + * @return none + */ +void UART1_BaudRateCfg(uint32_t baudrate) +{ + uint32_t x; + + x = 10 * GetSysClock() / 8 / baudrate; + x = (x + 5) / 10; + R16_UART1_DL = (uint16_t)x; +} + +/********************************************************************* + * @fn UART1_ByteTrigCfg + * + * @brief ֽڴж + * + * @param b - ֽ refer to UARTByteTRIGTypeDef + * + * @return none + */ +void UART1_ByteTrigCfg(UARTByteTRIGTypeDef b) +{ + R8_UART1_FCR = (R8_UART1_FCR & ~RB_FCR_FIFO_TRIG) | (b << 6); +} + +/********************************************************************* + * @fn UART1_INTCfg + * + * @brief ж + * + * @param s - жϿ״̬ǷʹӦж + * @param i - ж + * RB_IER_MODEM_CHG - ƽ״̬仯жʹλ UART0 ֧֣ + * RB_IER_LINE_STAT - ·״̬ж + * RB_IER_THR_EMPTY - ͱּĴж + * RB_IER_RECV_RDY - ж + * + * @return none + */ +void UART1_INTCfg(FunctionalState s, uint8_t i) +{ + if(s) + { + R8_UART1_IER |= i; + R8_UART1_MCR |= RB_MCR_INT_OE; + } + else + { + R8_UART1_IER &= ~i; + } +} + +/********************************************************************* + * @fn UART1_Reset + * + * @brief λ + * + * @param none + * + * @return none + */ +void UART1_Reset(void) +{ + R8_UART1_IER = RB_IER_RESET; +} + +/********************************************************************* + * @fn UART1_SendString + * + * @brief ڶֽڷ + * + * @param buf - ͵׵ַ + * @param l - ͵ݳ + * + * @return none + */ +void UART1_SendString(uint8_t *buf, uint16_t l) +{ + uint16_t len = l; + + while(len) + { + if(R8_UART1_TFC != UART_FIFO_SIZE) + { + R8_UART1_THR = *buf++; + len--; + } + } +} + +/********************************************************************* + * @fn UART1_RecvString + * + * @brief ڶȡֽ + * + * @param buf - ȡݴŻ׵ַ + * + * @return ȡݳ + */ +uint16_t UART1_RecvString(uint8_t *buf) +{ + uint16_t len = 0; + + while(R8_UART1_RFC) + { + *buf++ = R8_UART1_RBR; + len++; + } + + return (len); +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart2.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart2.c new file mode 100644 index 0000000..baceba9 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart2.c @@ -0,0 +1,151 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_uart2.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn UART2_DefInit + * + * @brief Ĭϳʼ + * + * @param none + * + * @return none + */ +void UART2_DefInit(void) +{ + UART2_BaudRateCfg(115200); + R8_UART2_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN; // FIFO򿪣4ֽ + R8_UART2_LCR = RB_LCR_WORD_SZ; + R8_UART2_IER = RB_IER_TXD_EN; + R8_UART2_DIV = 1; +} + +/********************************************************************* + * @fn UART2_BaudRateCfg + * + * @brief ڲ + * + * @param baudrate - + * + * @return none + */ +void UART2_BaudRateCfg(uint32_t baudrate) +{ + uint32_t x; + + x = 10 * GetSysClock() / 8 / baudrate; + x = (x + 5) / 10; + R16_UART2_DL = (uint16_t)x; +} + +/********************************************************************* + * @fn UART2_ByteTrigCfg + * + * @brief ֽڴж + * + * @param b - ֽ refer to UARTByteTRIGTypeDef + * + * @return none + */ +void UART2_ByteTrigCfg(UARTByteTRIGTypeDef b) +{ + R8_UART2_FCR = (R8_UART2_FCR & ~RB_FCR_FIFO_TRIG) | (b << 6); +} + +/********************************************************************* + * @fn UART2_INTCfg + * + * @brief ж + * + * @param s - жϿ״̬ǷʹӦж + * @param i - ж + * RB_IER_MODEM_CHG - ƽ״̬仯жʹλ UART0 ֧֣ + * RB_IER_LINE_STAT - ·״̬ж + * RB_IER_THR_EMPTY - ͱּĴж + * RB_IER_RECV_RDY - ж + * + * @return none + */ +void UART2_INTCfg(FunctionalState s, uint8_t i) +{ + if(s) + { + R8_UART2_IER |= i; + R8_UART2_MCR |= RB_MCR_INT_OE; + } + else + { + R8_UART2_IER &= ~i; + } +} + +/********************************************************************* + * @fn UART2_Reset + * + * @brief λ + * + * @param none + * + * @return none + */ +void UART2_Reset(void) +{ + R8_UART2_IER = RB_IER_RESET; +} + +/********************************************************************* + * @fn UART2_SendString + * + * @brief ڶֽڷ + * + * @param buf - ͵׵ַ + * @param l - ͵ݳ + * + * @return none + */ +void UART2_SendString(uint8_t *buf, uint16_t l) +{ + uint16_t len = l; + + while(len) + { + if(R8_UART2_TFC != UART_FIFO_SIZE) + { + R8_UART2_THR = *buf++; + len--; + } + } +} + +/********************************************************************* + * @fn UART2_RecvString + * + * @brief ڶȡֽ + * + * @param buf - ȡݴŻ׵ַ + * + * @return ȡݳ + */ +uint16_t UART2_RecvString(uint8_t *buf) +{ + uint16_t len = 0; + + while(R8_UART2_RFC) + { + *buf++ = R8_UART2_RBR; + len++; + } + + return (len); +} + diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart3.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart3.c new file mode 100644 index 0000000..165f07e --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_uart3.c @@ -0,0 +1,151 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_uart3.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +/********************************************************************* + * @fn UART3_DefInit + * + * @brief Ĭϳʼ + * + * @param none + * + * @return none + */ +void UART3_DefInit(void) +{ + UART3_BaudRateCfg(115200); + R8_UART3_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN; // FIFO򿪣4ֽ + R8_UART3_LCR = RB_LCR_WORD_SZ; + R8_UART3_IER = RB_IER_TXD_EN; + R8_UART3_DIV = 1; +} + +/********************************************************************* + * @fn UART3_BaudRateCfg + * + * @brief ڲ + * + * @param baudrate - + * + * @return none + */ +void UART3_BaudRateCfg(uint32_t baudrate) +{ + uint32_t x; + + x = 10 * GetSysClock() / 8 / baudrate; + x = (x + 5) / 10; + R16_UART3_DL = (uint16_t)x; +} + +/********************************************************************* + * @fn UART3_ByteTrigCfg + * + * @brief ֽڴж + * + * @param b - ֽ refer to UARTByteTRIGTypeDef + * + * @return none + */ +void UART3_ByteTrigCfg(UARTByteTRIGTypeDef b) +{ + R8_UART3_FCR = (R8_UART3_FCR & ~RB_FCR_FIFO_TRIG) | (b << 6); +} + +/********************************************************************* + * @fn UART3_INTCfg + * + * @brief ж + * + * @param s - жϿ״̬ǷʹӦж + * @param i - ж + * RB_IER_MODEM_CHG - ƽ״̬仯жʹλ UART0 ֧֣ + * RB_IER_LINE_STAT - ·״̬ж + * RB_IER_THR_EMPTY - ͱּĴж + * RB_IER_RECV_RDY - ж + * + * @return none + */ +void UART3_INTCfg(FunctionalState s, uint8_t i) +{ + if(s) + { + R8_UART3_IER |= i; + R8_UART3_MCR |= RB_MCR_INT_OE; + } + else + { + R8_UART3_IER &= ~i; + } +} + +/********************************************************************* + * @fn UART3_Reset + * + * @brief λ + * + * @param none + * + * @return none + */ +void UART3_Reset(void) +{ + R8_UART3_IER = RB_IER_RESET; +} + +/********************************************************************* + * @fn UART3_SendString + * + * @brief ڶֽڷ + * + * @param buf - ͵׵ַ + * @param l - ͵ݳ + * + * @return none + */ +void UART3_SendString(uint8_t *buf, uint16_t l) +{ + uint16_t len = l; + + while(len) + { + if(R8_UART3_TFC != UART_FIFO_SIZE) + { + R8_UART3_THR = *buf++; + len--; + } + } +} + +/********************************************************************* + * @fn UART3_RecvString + * + * @brief ڶȡֽ + * + * @param buf - ȡݴŻ׵ַ + * + * @return ȡݳ + */ +uint16_t UART3_RecvString(uint8_t *buf) +{ + uint16_t len = 0; + + while(R8_UART3_RFC) + { + *buf++ = R8_UART3_RBR; + len++; + } + + return (len); +} + diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2dev.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2dev.c new file mode 100644 index 0000000..02e9682 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2dev.c @@ -0,0 +1,113 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_usb2dev.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" + +uint8_t *pU2EP0_RAM_Addr; +uint8_t *pU2EP1_RAM_Addr; +uint8_t *pU2EP2_RAM_Addr; +uint8_t *pU2EP3_RAM_Addr; + +/********************************************************************* + * @fn USB2_DeviceInit + * + * @brief USB2豸ܳʼ4˵㣬8ͨ + * + * @param none + * + * @return none + */ +void USB2_DeviceInit(void) +{ + R8_USB2_CTRL = 0x00; // 趨ģʽ,ȡ RB_UC_CLR_ALL + + R8_U2EP4_1_MOD = RB_UEP4_RX_EN | RB_UEP4_TX_EN | RB_UEP1_RX_EN | RB_UEP1_TX_EN; // ˵4 OUT+IN,˵1 OUT+IN + R8_U2EP2_3_MOD = RB_UEP2_RX_EN | RB_UEP2_TX_EN | RB_UEP3_RX_EN | RB_UEP3_TX_EN; // ˵2 OUT+IN,˵3 OUT+IN + + R16_U2EP0_DMA = (uint16_t)(uint32_t)pU2EP0_RAM_Addr; + R16_U2EP1_DMA = (uint16_t)(uint32_t)pU2EP1_RAM_Addr; + R16_U2EP2_DMA = (uint16_t)(uint32_t)pU2EP2_RAM_Addr; + R16_U2EP3_DMA = (uint16_t)(uint32_t)pU2EP3_RAM_Addr; + + R8_U2EP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK; + R8_U2EP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG; + R8_U2EP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG; + R8_U2EP3_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG; + R8_U2EP4_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK; + + R8_USB2_DEV_AD = 0x00; + R8_USB2_CTRL = RB_UC_DEV_PU_EN | RB_UC_INT_BUSY | RB_UC_DMA_EN; // USB豸DMAжڼжϱ־δǰԶNAK + R16_PIN_ANALOG_IE |= RB_PIN_USB2_IE | RB_PIN_USB2_DP_PU; // ֹUSB˿ڸռ + R8_USB2_INT_FG = 0xFF; // жϱ־ + R8_U2DEV_CTRL = RB_UD_PD_DIS | RB_UD_PORT_EN; // USB˿ + R8_USB2_INT_EN = RB_UIE_SUSPEND | RB_UIE_BUS_RST | RB_UIE_TRANSFER; +} + +/********************************************************************* + * @fn U2DevEP1_IN_Deal + * + * @brief U2˵1ϴ + * + * @param l - ϴݳ(<64B) + * + * @return none + */ +void U2DevEP1_IN_Deal(uint8_t l) +{ + R8_U2EP1_T_LEN = l; + R8_U2EP1_CTRL = (R8_U2EP1_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_ACK; +} + +/********************************************************************* + * @fn U2DevEP2_IN_Deal + * + * @brief U2˵2ϴ + * + * @param l - ϴݳ(<64B) + * + * @return none + */ +void U2DevEP2_IN_Deal(uint8_t l) +{ + R8_U2EP2_T_LEN = l; + R8_U2EP2_CTRL = (R8_U2EP2_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_ACK; +} + +/********************************************************************* + * @fn U2DevEP3_IN_Deal + * + * @brief U2˵3ϴ + * + * @param l - ϴݳ(<64B) + * + * @return none + */ +void U2DevEP3_IN_Deal(uint8_t l) +{ + R8_U2EP3_T_LEN = l; + R8_U2EP3_CTRL = (R8_U2EP3_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_ACK; +} + +/********************************************************************* + * @fn U2DevEP4_IN_Deal + * + * @brief U2˵4ϴ + * + * @param l - ϴݳ(<64B) + * + * @return none + */ +void U2DevEP4_IN_Deal(uint8_t l) +{ + R8_U2EP4_T_LEN = l; + R8_U2EP4_CTRL = (R8_U2EP4_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_ACK; +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2hostBase.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2hostBase.c new file mode 100644 index 0000000..6aad878 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2hostBase.c @@ -0,0 +1,652 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_usbhost.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" +#if DISK_LIB_ENABLE + #include "CHRV3UFI.H" +#endif + +uint8_t Usb2DevEndp0Size; // USB2豸Ķ˵0ߴ +uint8_t FoundNewU2Dev; +_RootHubDev ThisUsb2Dev; //ROOT +_DevOnHubPort DevOnU2HubPort[HUB_MAX_PORTS]; // ٶ:1ⲿHUB,ÿⲿHUBHUB_MAX_PORTS˿(˲) + +uint8_t *pU2HOST_RX_RAM_Addr; +uint8_t *pU2HOST_TX_RAM_Addr; + +/*ȡ豸*/ +__attribute__((aligned(4))) const uint8_t SetupGetU2DevDescr[] = {USB_REQ_TYP_IN, USB_GET_DESCRIPTOR, 0x00, + USB_DESCR_TYP_DEVICE, 0x00, 0x00, sizeof(USB_DEV_DESCR), 0x00}; +/*ȡ*/ +__attribute__((aligned(4))) const uint8_t SetupGetU2CfgDescr[] = {USB_REQ_TYP_IN, USB_GET_DESCRIPTOR, 0x00, + USB_DESCR_TYP_CONFIG, 0x00, 0x00, 0x04, 0x00}; +/*USBַ*/ +__attribute__((aligned(4))) const uint8_t SetupSetUsb2Addr[] = {USB_REQ_TYP_OUT, USB_SET_ADDRESS, USB_DEVICE_ADDR, + 0x00, 0x00, 0x00, 0x00, 0x00}; +/*USB*/ +__attribute__((aligned(4))) const uint8_t SetupSetUsb2Config[] = {USB_REQ_TYP_OUT, USB_SET_CONFIGURATION, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +/*USBӿ*/ +__attribute__((aligned(4))) const uint8_t SetupSetUsb2Interface[] = {USB_REQ_RECIP_INTERF, USB_SET_INTERFACE, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +/*˵STALL*/ +__attribute__((aligned(4))) const uint8_t SetupClrU2EndpStall[] = {USB_REQ_TYP_OUT | USB_REQ_RECIP_ENDP, USB_CLEAR_FEATURE, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/********************************************************************* + * @fn DisableRootU2HubPort + * + * @brief رROOT-HUB˿,ʵӲѾԶر,˴ֻһЩṹ״̬ + * + * @param none + * + * @return none + */ +void DisableRootU2HubPort(void) +{ +#ifdef FOR_ROOT_UDISK_ONLY + CHRV3DiskStatus = DISK_DISCONNECT; +#endif +#ifndef DISK_BASE_BUF_LEN + ThisUsb2Dev.DeviceStatus = ROOT_DEV_DISCONNECT; + ThisUsb2Dev.DeviceAddress = 0x00; +#endif +} + +/********************************************************************* + * @fn AnalyzeRootU2Hub + * + * @brief ROOT-HUB״̬,ROOT-HUB˿ڵ豸¼ + * 豸γ,еDisableRootHubPort(),˿ڹر,¼,Ӧ˿ڵ״̬λ + * + * @param none + * + * @return ERR_SUCCESSΪû,ERR_USB_CONNECTΪ⵽,ERR_USB_DISCONΪ⵽Ͽ + */ +uint8_t AnalyzeRootU2Hub(void) +{ + uint8_t s; + + s = ERR_SUCCESS; + + if(R8_USB2_MIS_ST & RB_UMS_DEV_ATTACH) + { // 豸 +#ifdef DISK_BASE_BUF_LEN + if(CHRV3DiskStatus == DISK_DISCONNECT +#else + if(ThisUsb2Dev.DeviceStatus == ROOT_DEV_DISCONNECT // ⵽豸 +#endif + || (R8_U2HOST_CTRL & RB_UH_PORT_EN) == 0x00) + { // ⵽豸,δ,˵Ǹղ + DisableRootU2HubPort(); // رն˿ +#ifdef DISK_BASE_BUF_LEN + CHRV3DiskStatus = DISK_CONNECT; +#else + ThisUsb2Dev.DeviceSpeed = R8_USB2_MIS_ST & RB_UMS_DM_LEVEL ? 0 : 1; + ThisUsb2Dev.DeviceStatus = ROOT_DEV_CONNECTED; //ӱ־ +#endif + PRINT("USB2 dev in\n"); + s = ERR_USB_CONNECT; + } + } + +#ifdef DISK_BASE_BUF_LEN + else if(CHRV3DiskStatus >= DISK_CONNECT) + { +#else + else if(ThisUsb2Dev.DeviceStatus >= ROOT_DEV_CONNECTED) + { //⵽豸γ +#endif + DisableRootU2HubPort(); // رն˿ + PRINT("USB2 dev out\n"); + if(s == ERR_SUCCESS) + s = ERR_USB_DISCON; + } + // R8_USB_INT_FG = RB_UIF_DETECT; // жϱ־ + return (s); +} + +/********************************************************************* + * @fn SetHostUsb2Addr + * + * @brief USBǰUSB豸ַ + * + * @param addr - USB豸ַ + * + * @return none + */ +void SetHostUsb2Addr(uint8_t addr) +{ + R8_USB2_DEV_AD = (R8_USB2_DEV_AD & RB_UDA_GP_BIT) | (addr & MASK_USB_ADDR); +} + +/********************************************************************* + * @fn SetUsb2Speed + * + * @brief õǰUSBٶ + * + * @param FullSpeed - USBٶ + * + * @return none + */ +void SetUsb2Speed(uint8_t FullSpeed) +{ +#ifndef DISK_BASE_BUF_LEN + if(FullSpeed) // ȫ + { + R8_USB2_CTRL &= ~RB_UC_LOW_SPEED; // ȫ + R8_U2H_SETUP &= ~RB_UH_PRE_PID_EN; // ֹPRE PID + } + else + { + R8_USB2_CTRL |= RB_UC_LOW_SPEED; // + } +#endif + (void)FullSpeed; +} + +/********************************************************************* + * @fn ResetRootU2HubPort + * + * @brief ⵽豸,λ,Ϊö豸׼,ΪĬΪȫ + * + * @param none + * + * @return none + */ +void ResetRootU2HubPort(void) +{ + Usb2DevEndp0Size = DEFAULT_ENDP0_SIZE; //USB2豸Ķ˵0ߴ + SetHostUsb2Addr(0x00); + R8_U2HOST_CTRL &= ~RB_UH_PORT_EN; // ص˿ + SetUsb2Speed(1); // ĬΪȫ + R8_U2HOST_CTRL = (R8_U2HOST_CTRL & ~RB_UH_LOW_SPEED) | RB_UH_BUS_RESET; // ĬΪȫ,ʼλ + mDelaymS(15); // λʱ10mS20mS + R8_U2HOST_CTRL = R8_U2HOST_CTRL & ~RB_UH_BUS_RESET; // λ + mDelayuS(250); + R8_USB2_INT_FG = RB_UIF_DETECT; // жϱ־ +} + +/********************************************************************* + * @fn EnableRootU2HubPort + * + * @brief ʹROOT-HUB˿,ӦbUH_PORT_EN1˿,豸Ͽܵ·ʧ + * + * @param none + * + * @return ERR_SUCCESSΪ⵽,ERR_USB_DISCONΪ + */ +uint8_t EnableRootU2HubPort(void) +{ +#ifdef DISK_BASE_BUF_LEN + if(CHRV3DiskStatus < DISK_CONNECT) + CHRV3DiskStatus = DISK_CONNECT; +#else + if(ThisUsb2Dev.DeviceStatus < ROOT_DEV_CONNECTED) + ThisUsb2Dev.DeviceStatus = ROOT_DEV_CONNECTED; +#endif + if(R8_USB2_MIS_ST & RB_UMS_DEV_ATTACH) + { // 豸 +#ifndef DISK_BASE_BUF_LEN + if((R8_U2HOST_CTRL & RB_UH_PORT_EN) == 0x00) + { // δʹ + ThisUsb2Dev.DeviceSpeed = (R8_USB2_MIS_ST & RB_UMS_DM_LEVEL) ? 0 : 1; + if(ThisUsb2Dev.DeviceSpeed == 0) + R8_U2HOST_CTRL |= RB_UH_LOW_SPEED; // + } +#endif + R8_U2HOST_CTRL |= RB_UH_PORT_EN; //ʹHUB˿ + return (ERR_SUCCESS); + } + return (ERR_USB_DISCON); +} + +#ifndef DISK_BASE_BUF_LEN +/********************************************************************* + * @fn SelectU2HubPort + * + * @brief ѡҪHUB + * + * @param HubPortIndex - ѡָROOT-HUB˿ڵⲿHUBָ˿ + * + * @return None + */ +void SelectU2HubPort(uint8_t HubPortIndex) +{ + if(HubPortIndex) // ѡָROOT-HUB˿ڵⲿHUBָ˿ + { + SetHostUsb2Addr(DevOnU2HubPort[HubPortIndex - 1].DeviceAddress); // USBǰUSB豸ַ + SetUsb2Speed(DevOnU2HubPort[HubPortIndex - 1].DeviceSpeed); // õǰUSBٶ + if(DevOnU2HubPort[HubPortIndex - 1].DeviceSpeed == 0) // ͨⲿHUBUSB豸ͨѶҪǰID + { + R8_U2EP1_CTRL |= RB_UH_PRE_PID_EN; // PRE PID + mDelayuS(100); + } + } + else + { + SetHostUsb2Addr(ThisUsb2Dev.DeviceAddress); // USBǰUSB豸ַ + SetUsb2Speed(ThisUsb2Dev.DeviceSpeed); // USB豸ٶ + } +} +#endif + +/********************************************************************* + * @fn WaitUSB2_Interrupt + * + * @brief ȴUSBж + * + * @param none + * + * @return ERR_SUCCESS ݽջ߷ͳɹ,ERR_USB_UNKNOWN ݽջ߷ʧ + */ +uint8_t WaitUSB2_Interrupt(void) +{ + uint16_t i; + for(i = WAIT_USB_TOUT_200US; i != 0 && (R8_USB2_INT_FG & RB_UIF_TRANSFER) == 0; i--) + { + ; + } + return ((R8_USB2_INT_FG & RB_UIF_TRANSFER) ? ERR_SUCCESS : ERR_USB_UNKNOWN); +} + +/********************************************************************* + * @fn USB2HostTransact + * + * @brief ,ĿĶ˵ַ/PID,ͬ־,20uSΪλNAKʱ(0,0xFFFF),0ɹ,ʱ/ + * ӳ,ʵӦ,Ϊṩٶ,ӦöԱӳŻ + * + * @param endp_pid - ƺ͵ַ, 4λtoken_pid, 4λǶ˵ַ + * @param tog - ͬ־ + * @param timeout - ʱʱ + * + * @return ERR_USB_UNKNOWN ʱӲ쳣 + * ERR_USB_DISCON 豸Ͽ + * ERR_USB_CONNECT 豸 + * ERR_SUCCESS + */ +uint8_t USB2HostTransact(uint8_t endp_pid, uint8_t tog, uint32_t timeout) +{ + uint8_t TransRetry; + + uint8_t s, r; + uint16_t i; + + R8_U2H_RX_CTRL = R8_U2H_TX_CTRL = tog; + TransRetry = 0; + + do + { + R8_U2H_EP_PID = endp_pid; // ָPIDĿĶ˵ + R8_USB2_INT_FG = RB_UIF_TRANSFER; + for(i = WAIT_USB_TOUT_200US; i != 0 && (R8_USB2_INT_FG & RB_UIF_TRANSFER) == 0; i--) + ; + R8_U2H_EP_PID = 0x00; // ֹͣUSB + if((R8_USB2_INT_FG & RB_UIF_TRANSFER) == 0) + { + return (ERR_USB_UNKNOWN); + } + + if(R8_USB2_INT_FG & RB_UIF_DETECT) + { // USB豸¼ + // mDelayuS( 200 ); // ȴ + R8_USB2_INT_FG = RB_UIF_DETECT; + s = AnalyzeRootU2Hub(); // ROOT-U2HUB״̬ + + if(s == ERR_USB_CONNECT) + FoundNewU2Dev = 1; +#ifdef DISK_BASE_BUF_LEN + if(CHRV3DiskStatus == DISK_DISCONNECT) + { + return (ERR_USB_DISCON); + } // USB豸Ͽ¼ + if(CHRV3DiskStatus == DISK_CONNECT) + { + return (ERR_USB_CONNECT); + } // USB豸¼ +#else + if(ThisUsb2Dev.DeviceStatus == ROOT_DEV_DISCONNECT) + { + return (ERR_USB_DISCON); + } // USB豸Ͽ¼ + if(ThisUsb2Dev.DeviceStatus == ROOT_DEV_CONNECTED) + { + return (ERR_USB_CONNECT); + } // USB豸¼ +#endif + mDelayuS(200); // ȴ + } + + if(R8_USB2_INT_FG & RB_UIF_TRANSFER) // ¼ + { + if(R8_USB2_INT_ST & RB_UIS_TOG_OK) + { + return (ERR_SUCCESS); + } + r = R8_USB2_INT_ST & MASK_UIS_H_RES; // USB豸Ӧ״̬ + if(r == USB_PID_STALL) + { + return (r | ERR_USB_TRANSFER); + } + if(r == USB_PID_NAK) + { + if(timeout == 0) + { + return (r | ERR_USB_TRANSFER); + } + if(timeout < 0xFFFFFFFF) + timeout--; + --TransRetry; + } + else + switch(endp_pid >> 4) + { + case USB_PID_SETUP: + case USB_PID_OUT: + if(r) + { + return (r | ERR_USB_TRANSFER); + } // dzʱ/,Ӧ + break; // ʱ + case USB_PID_IN: + if(r == USB_PID_DATA0 || r == USB_PID_DATA1) + { // ͬ趪 + } // ͬ + else if(r) + { + return (r | ERR_USB_TRANSFER); + } // dzʱ/,Ӧ + break; // ʱ + default: + return (ERR_USB_UNKNOWN); // ܵ + break; + } + } + else + { // ж,Ӧ÷ + R8_USB2_INT_FG = 0xFF; /* жϱ־ */ + } + mDelayuS(15); + } while(++TransRetry < 3); + return (ERR_USB_TRANSFER); // Ӧʱ +} + +/********************************************************************* + * @fn U2HostCtrlTransfer + * + * @brief ִпƴ,8ֽpSetupReq,DataBufΪѡշ + * + * @param DataBuf - Ҫպͷ,ôDataBufָЧڴź + * @param RetLen - ʵʳɹշܳȱRetLenָֽڱ + * + * @return ERR_USB_BUF_OVER IN״̬׶γ + * ERR_SUCCESS ݽɹ + */ +uint8_t U2HostCtrlTransfer(uint8_t *DataBuf, uint8_t *RetLen) +{ + uint16_t RemLen = 0; + uint8_t s, RxLen, RxCnt, TxCnt; + uint8_t *pBuf; + uint8_t *pLen; + + pBuf = DataBuf; + pLen = RetLen; + mDelayuS(200); + if(pLen) + *pLen = 0; // ʵʳɹշܳ + + R8_U2H_TX_LEN = sizeof(USB_SETUP_REQ); + s = USB2HostTransact(USB_PID_SETUP << 4 | 0x00, 0x00, 200000 / 20); // SETUP׶,200mSʱ + if(s != ERR_SUCCESS) + return (s); + R8_U2H_RX_CTRL = R8_U2H_TX_CTRL = RB_UH_R_TOG | RB_UH_R_AUTO_TOG | RB_UH_T_TOG | RB_UH_T_AUTO_TOG; // ĬDATA1 + R8_U2H_TX_LEN = 0x01; // Ĭݹ״̬׶ΪIN + RemLen = pU2SetupReq->wLength; + if(RemLen && pBuf) // Ҫշ + { + if(pU2SetupReq->bRequestType & USB_REQ_TYP_IN) // + { + while(RemLen) + { + mDelayuS(200); + s = USB2HostTransact(USB_PID_IN << 4 | 0x00, R8_U2H_RX_CTRL, 200000 / 20); // IN + if(s != ERR_SUCCESS) + return (s); + RxLen = R8_USB2_RX_LEN < RemLen ? R8_USB2_RX_LEN : RemLen; + RemLen -= RxLen; + if(pLen) + *pLen += RxLen; // ʵʳɹշܳ + for(RxCnt = 0; RxCnt != RxLen; RxCnt++) + { + *pBuf = pU2HOST_RX_RAM_Addr[RxCnt]; + pBuf++; + } + if(R8_USB2_RX_LEN == 0 || (R8_USB2_RX_LEN & (Usb2DevEndp0Size - 1))) + break; // ̰ + } + R8_U2H_TX_LEN = 0x00; // ״̬׶ΪOUT + } + else // + { + while(RemLen) + { + mDelayuS(200); + R8_U2H_TX_LEN = RemLen >= Usb2DevEndp0Size ? Usb2DevEndp0Size : RemLen; + for(TxCnt = 0; TxCnt != R8_U2H_TX_LEN; TxCnt++) + { + pU2HOST_TX_RAM_Addr[TxCnt] = *pBuf; + pBuf++; + } + s = USB2HostTransact(USB_PID_OUT << 4 | 0x00, R8_U2H_TX_CTRL, 200000 / 20); // OUT + if(s != ERR_SUCCESS) + return (s); + RemLen -= R8_U2H_TX_LEN; + if(pLen) + *pLen += R8_U2H_TX_LEN; // ʵʳɹշܳ + } + // R8_U2H_TX_LEN = 0x01; // ״̬׶ΪIN + } + } + mDelayuS(200); + s = USB2HostTransact((R8_U2H_TX_LEN ? USB_PID_IN << 4 | 0x00 : USB_PID_OUT << 4 | 0x00), RB_UH_R_TOG | RB_UH_T_TOG, 200000 / 20); // STATUS׶ + if(s != ERR_SUCCESS) + return (s); + if(R8_U2H_TX_LEN == 0) + return (ERR_SUCCESS); // ״̬OUT + if(R8_USB2_RX_LEN == 0) + return (ERR_SUCCESS); // ״̬IN,IN״̬ݳ + return (ERR_USB_BUF_OVER); // IN״̬׶δ +} + +/********************************************************************* + * @fn CopyU2SetupReqPkg + * + * @brief ƿƴ + * + * @param pReqPkt - ַ + * + * @return none + */ +void CopyU2SetupReqPkg(const uint8_t *pReqPkt) // ƿƴ +{ + uint8_t i; + for(i = 0; i != sizeof(USB_SETUP_REQ); i++) + { + ((uint8_t *)pU2SetupReq)[i] = *pReqPkt; + pReqPkt++; + } +} + +/********************************************************************* + * @fn CtrlGetU2DeviceDescr + * + * @brief ȡ豸, pHOST_TX_RAM_Addr + * + * @param none + * + * @return ERR_USB_BUF_OVER ȴ + * ERR_SUCCESS ɹ + */ +uint8_t CtrlGetU2DeviceDescr(void) +{ + uint8_t s; + uint8_t len; + + Usb2DevEndp0Size = DEFAULT_ENDP0_SIZE; + CopyU2SetupReqPkg((uint8_t *)SetupGetU2DevDescr); + s = U2HostCtrlTransfer(U2Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + return (s); + Usb2DevEndp0Size = ((PUSB_DEV_DESCR)U2Com_Buffer)->bMaxPacketSize0; // ˵0,Ǽ򻯴,ӦȻȡǰ8ֽںUsbDevEndp0Sizeټ + if(len < ((PUSB_SETUP_REQ)SetupGetU2DevDescr)->wLength) + return (ERR_USB_BUF_OVER); // ȴ + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn CtrlGetU2ConfigDescr + * + * @brief ȡ, pHOST_TX_RAM_Addr + * + * @param none + * + * @return ERR_USB_BUF_OVER ȴ + * ERR_SUCCESS ɹ + */ +uint8_t CtrlGetU2ConfigDescr(void) +{ + uint8_t s; + uint8_t len; + + CopyU2SetupReqPkg((uint8_t *)SetupGetU2CfgDescr); + s = U2HostCtrlTransfer(U2Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + return (s); + if(len < ((PUSB_SETUP_REQ)SetupGetU2CfgDescr)->wLength) + return (ERR_USB_BUF_OVER); // سȴ + + len = ((PUSB_CFG_DESCR)U2Com_Buffer)->wTotalLength; + CopyU2SetupReqPkg((uint8_t *)SetupGetU2CfgDescr); + pU2SetupReq->wLength = len; // ܳ + s = U2HostCtrlTransfer(U2Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + return (s); + +#ifdef DISK_BASE_BUF_LEN + if(len > 64) + len = 64; + memcpy(TxBuffer, U2Com_Buffer, len); //U̲ʱҪTxBuffer +#endif + + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn CtrlSetUsb2Address + * + * @brief USB豸ַ + * + * @param addr - 豸ַ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlSetUsb2Address(uint8_t addr) +{ + uint8_t s; + + CopyU2SetupReqPkg((uint8_t *)SetupSetUsb2Addr); + pU2SetupReq->wValue = addr; // USB豸ַ + s = U2HostCtrlTransfer(NULL, NULL); // ִпƴ + if(s != ERR_SUCCESS) + return (s); + SetHostUsb2Addr(addr); // USBǰUSB豸ַ + mDelaymS(10); // ȴUSB豸ɲ + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn CtrlSetUsb2Config + * + * @brief USB豸 + * + * @param cfg - ֵ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlSetUsb2Config(uint8_t cfg) +{ + CopyU2SetupReqPkg((uint8_t *)SetupSetUsb2Config); + pU2SetupReq->wValue = cfg; // USB豸 + return (U2HostCtrlTransfer(NULL, NULL)); // ִпƴ +} + +/********************************************************************* + * @fn CtrlClearU2EndpStall + * + * @brief ˵STALL + * + * @param endp - ˵ַ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlClearU2EndpStall(uint8_t endp) +{ + CopyU2SetupReqPkg((uint8_t *)SetupClrU2EndpStall); // ˵Ĵ + pU2SetupReq->wIndex = endp; // ˵ַ + return (U2HostCtrlTransfer(NULL, NULL)); // ִпƴ +} + +/********************************************************************* + * @fn CtrlSetUsb2Intercace + * + * @brief USB豸ӿ + * + * @param cfg - ֵ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlSetUsb2Intercace(uint8_t cfg) +{ + CopyU2SetupReqPkg((uint8_t *)SetupSetUsb2Interface); + pU2SetupReq->wValue = cfg; // USB豸 + return (U2HostCtrlTransfer(NULL, NULL)); // ִпƴ +} + +/********************************************************************* + * @fn USB2_HostInit + * + * @brief USBܳʼ + * + * @param none + * + * @return none + */ +void USB2_HostInit(void) +{ + R8_USB2_CTRL = RB_UC_HOST_MODE; + R8_U2HOST_CTRL = 0; + R8_USB2_DEV_AD = 0x00; + + R8_U2H_EP_MOD = RB_UH_EP_TX_EN | RB_UH_EP_RX_EN; + R16_U2H_RX_DMA = (uint16_t)(uint32_t)pU2HOST_RX_RAM_Addr; + R16_U2H_TX_DMA = (uint16_t)(uint32_t)pU2HOST_TX_RAM_Addr; + + R8_U2H_RX_CTRL = 0x00; + R8_U2H_TX_CTRL = 0x00; + R8_USB2_CTRL = RB_UC_HOST_MODE | RB_UC_INT_BUSY | RB_UC_DMA_EN; + R8_U2H_SETUP = RB_UH_SOF_EN; + R8_USB2_INT_FG = 0xFF; + DisableRootU2HubPort(); + R8_USB2_INT_EN = RB_UIE_TRANSFER | RB_UIE_DETECT; + + FoundNewU2Dev = 0; +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2hostClass.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2hostClass.c new file mode 100644 index 0000000..55d38fb --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usb2hostClass.c @@ -0,0 +1,826 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_usbhost.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" +#if DISK_LIB_ENABLE + #include "CHRV3UFI.H" +#endif + +/* HIDϴ */ +__attribute__((aligned(4))) const uint8_t SetupSetU2HIDIdle[] = {0x21, HID_SET_IDLE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +/* ȡHID豸 */ +__attribute__((aligned(4))) const uint8_t SetupGetU2HIDDevReport[] = {0x81, USB_GET_DESCRIPTOR, 0x00, + USB_DESCR_TYP_REPORT, 0x00, 0x00, 0x41, 0x00}; +/* ȡHUB */ +__attribute__((aligned(4))) const uint8_t SetupGetU2HubDescr[] = {HUB_GET_HUB_DESCRIPTOR, HUB_GET_DESCRIPTOR, + 0x00, USB_DESCR_TYP_HUB, 0x00, 0x00, sizeof(USB_HUB_DESCR), 0x00}; + +__attribute__((aligned(4))) uint8_t U2Com_Buffer[128]; // ûʱ,öʱڴ,öٽҲͨʱ + +/********************************************************************* + * @fn AnalyzeU2HidIntEndp + * + * @brief зHIDж϶˵ĵַ,HubPortIndex0浽ROOTHUBǷֵ򱣴浽HUB½ṹ + * + * @param buf - ݻַ HubPortIndex0ʾHUB0ʾⲿHUBµĶ˿ں + * + * @return ˵ + */ +uint8_t AnalyzeU2HidIntEndp(uint8_t *buf, uint8_t HubPortIndex) +{ + uint8_t i, s, l; + s = 0; + + if(HubPortIndex) + { + memset(DevOnU2HubPort[HubPortIndex - 1].GpVar, 0, sizeof(DevOnU2HubPort[HubPortIndex - 1].GpVar)); // + } + else + { + memset(ThisUsb2Dev.GpVar, 0, sizeof(ThisUsb2Dev.GpVar)); // + } + + for(i = 0; i < ((PUSB_CFG_DESCR)buf)->wTotalLength; i += l) // ж϶˵,ͽӿ + { + if(((PUSB_ENDP_DESCR)(buf + i))->bDescriptorType == USB_DESCR_TYP_ENDP // Ƕ˵ + && (((PUSB_ENDP_DESCR)(buf + i))->bmAttributes & USB_ENDP_TYPE_MASK) == USB_ENDP_TYPE_INTER // ж϶˵ + && (((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_DIR_MASK)) // IN˵ + { // ж϶˵ĵַ,λ7ͬ־λ,0 + if(HubPortIndex) + { + DevOnU2HubPort[HubPortIndex - 1].GpVar[s] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; + } + else + { + ThisUsb2Dev.GpVar[s] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; // ж϶˵ĵַԸҪwMaxPacketSizebInterval + } + PRINT("%02x ", (uint16_t)ThisUsb2Dev.GpVar[s]); + s++; + if(s >= 4) + { + break; //ֻ4˵ + } + } + l = ((PUSB_ENDP_DESCR)(buf + i))->bLength; // ǰ, + if(l > 16) + { + break; + } + } + PRINT("\n"); + return (s); +} + +/********************************************************************* + * @fn AnalyzeU2BulkEndp + * + * @brief ˵,GpVar[0]GpVar[1]ϴ˵㡣GpVar[2]GpVar[3]´˵ + * + * @param buf - ݻַ HubPortIndex0ʾHUB0ʾⲿHUBµĶ˿ں + * + * @return 0 + */ +uint8_t AnalyzeU2BulkEndp(uint8_t *buf, uint8_t HubPortIndex) +{ + uint8_t i, s1, s2, l; + s1 = 0; + s2 = 2; + + if(HubPortIndex) + { + memset(DevOnU2HubPort[HubPortIndex - 1].GpVar, 0, sizeof(DevOnU2HubPort[HubPortIndex - 1].GpVar)); // + } + else + { + memset(ThisUsb2Dev.GpVar, 0, sizeof(ThisUsb2Dev.GpVar)); // + } + + for(i = 0; i < ((PUSB_CFG_DESCR)buf)->wTotalLength; i += l) // ж϶˵,ͽӿ + { + if((((PUSB_ENDP_DESCR)(buf + i))->bDescriptorType == USB_DESCR_TYP_ENDP) // Ƕ˵ + && ((((PUSB_ENDP_DESCR)(buf + i))->bmAttributes & USB_ENDP_TYPE_MASK) == USB_ENDP_TYPE_BULK)) // ж϶˵ + + { + if(HubPortIndex) + { + if(((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_DIR_MASK) + { + DevOnU2HubPort[HubPortIndex - 1].GpVar[s1++] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; + } + else + { + DevOnU2HubPort[HubPortIndex - 1].GpVar[s2++] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; + } + } + else + { + if(((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_DIR_MASK) + { + ThisUsb2Dev.GpVar[s1++] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; + } + else + { + ThisUsb2Dev.GpVar[s2++] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; + } + } + + if(s1 == 2) + { + s1 = 1; + } + if(s2 == 4) + { + s2 = 3; + } + } + l = ((PUSB_ENDP_DESCR)(buf + i))->bLength; // ǰ, + if(l > 16) + { + break; + } + } + return (0); +} + +/********************************************************************* + * @fn InitRootU2Device + * + * @brief ʼָROOT-HUB˿ڵUSB豸 + * + * @param none + * + * @return + */ +uint8_t InitRootU2Device(void) +{ + uint8_t i, s; + uint8_t cfg, dv_cls, if_cls; + + PRINT("Reset U2 host port\n"); + ResetRootU2HubPort(); // ⵽豸,λӦ˿ڵUSB + for(i = 0, s = 0; i < 100; i++) + { // ȴUSB豸λ,100mSʱ + mDelaymS(1); + if(EnableRootU2HubPort() == ERR_SUCCESS) + { // ʹܶ˿ + i = 0; + s++; + if(s > 100) + break; // Ѿȶ100mS + } + } + if(i) + { // λ豸û + DisableRootU2HubPort(); + PRINT("Disable U2 host port because of disconnect\n"); + return (ERR_USB_DISCON); + } + SetUsb2Speed(ThisUsb2Dev.DeviceSpeed); // õǰUSBٶ + + PRINT("GetU2DevDescr: "); + s = CtrlGetU2DeviceDescr(); // ȡ豸 + if(s == ERR_SUCCESS) + { + for(i = 0; i < ((PUSB_SETUP_REQ)SetupGetU2DevDescr)->wLength; i++) + PRINT("x%02X ", (uint16_t)(U2Com_Buffer[i])); + PRINT("\n"); + + ThisUsb2Dev.DeviceVID = ((PUSB_DEV_DESCR)U2Com_Buffer)->idVendor; //VID PIDϢ + ThisUsb2Dev.DevicePID = ((PUSB_DEV_DESCR)U2Com_Buffer)->idProduct; + dv_cls = ((PUSB_DEV_DESCR)U2Com_Buffer)->bDeviceClass; + + s = CtrlSetUsb2Address(((PUSB_SETUP_REQ)SetupSetUsb2Addr)->wValue); + if(s == ERR_SUCCESS) + { + ThisUsb2Dev.DeviceAddress = ((PUSB_SETUP_REQ)SetupSetUsb2Addr)->wValue; // USBַ + + PRINT("GetU2CfgDescr: "); + s = CtrlGetU2ConfigDescr(); + if(s == ERR_SUCCESS) + { + for(i = 0; i < ((PUSB_CFG_DESCR)U2Com_Buffer)->wTotalLength; i++) + { + PRINT("x%02X ", (uint16_t)(U2Com_Buffer[i])); + } + PRINT("\n"); + /* ,ȡ˵/˵ַ/˵С,±endp_addrendp_size */ + cfg = ((PUSB_CFG_DESCR)U2Com_Buffer)->bConfigurationValue; + if_cls = ((PUSB_CFG_DESCR_LONG)U2Com_Buffer)->itf_descr.bInterfaceClass; // ӿ + + if((dv_cls == 0x00) && (if_cls == USB_DEV_CLASS_STORAGE)) + { // USB洢豸,ȷU +#ifdef FOR_ROOT_UDISK_ONLY + CHRV3DiskStatus = DISK_USB_ADDR; + return (ERR_SUCCESS); + } + else + return (ERR_USB_UNSUPPORT); +#else + s = CtrlSetUsb2Config(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + ThisUsb2Dev.DeviceStatus = ROOT_DEV_SUCCESS; + ThisUsb2Dev.DeviceType = USB_DEV_CLASS_STORAGE; + PRINT("U2 USB-Disk Ready\n"); + SetUsb2Speed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + } + else if((dv_cls == 0x00) && (if_cls == USB_DEV_CLASS_PRINTER) && ((PUSB_CFG_DESCR_LONG)U2Com_Buffer)->itf_descr.bInterfaceSubClass == 0x01) + { // Ǵӡ豸 + s = CtrlSetUsb2Config(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + // 豣˵ϢԱUSB + ThisUsb2Dev.DeviceStatus = ROOT_DEV_SUCCESS; + ThisUsb2Dev.DeviceType = USB_DEV_CLASS_PRINTER; + PRINT("U2 USB-Print Ready\n"); + SetUsb2Speed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + } + else if((dv_cls == 0x00) && (if_cls == USB_DEV_CLASS_HID) && ((PUSB_CFG_DESCR_LONG)U2Com_Buffer)->itf_descr.bInterfaceSubClass <= 0x01) + { // HID豸,/ + // зHIDж϶˵ĵַ + s = AnalyzeU2HidIntEndp(U2Com_Buffer, 0); // зHIDж϶˵ĵַ + PRINT("AnalyzeU2HidIntEndp %02x\n", (uint16_t)s); + // ж϶˵ĵַ,λ7ͬ־λ,0 + if_cls = ((PUSB_CFG_DESCR_LONG)U2Com_Buffer)->itf_descr.bInterfaceProtocol; + s = CtrlSetUsb2Config(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + // Set_Idle( ); + // 豣˵ϢԱUSB + ThisUsb2Dev.DeviceStatus = ROOT_DEV_SUCCESS; + if(if_cls == 1) + { + ThisUsb2Dev.DeviceType = DEV_TYPE_KEYBOARD; + // һʼ,豸ָʾLED + PRINT("U2 USB-Keyboard Ready\n"); + SetUsb2Speed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + else if(if_cls == 2) + { + ThisUsb2Dev.DeviceType = DEV_TYPE_MOUSE; + // ΪԺѯ״̬,Ӧ÷,ȡж϶˿ڵĵַ,ȵϢ + PRINT("U2 USB-Mouse Ready\n"); + SetUsb2Speed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + s = ERR_USB_UNSUPPORT; + } + } + else if(dv_cls == USB_DEV_CLASS_HUB) + { // HUB豸, + s = CtrlGetU2HubDescr(); + if(s == ERR_SUCCESS) + { + PRINT("Max Port:%02X ", (((PXUSB_HUB_DESCR)U2Com_Buffer)->bNbrPorts)); + ThisUsb2Dev.GpHUBPortNum = ((PXUSB_HUB_DESCR)U2Com_Buffer)->bNbrPorts; // HUBĶ˿ + if(ThisUsb2Dev.GpHUBPortNum > HUB_MAX_PORTS) + { + ThisUsb2Dev.GpHUBPortNum = HUB_MAX_PORTS; // ΪṹDevOnHubPortʱΪٶÿHUBHUB_MAX_PORTS˿ + } + s = CtrlSetUsb2Config(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + ThisUsb2Dev.DeviceStatus = ROOT_DEV_SUCCESS; + ThisUsb2Dev.DeviceType = USB_DEV_CLASS_HUB; + //豣˵ϢԱUSB,ж϶˵HUB¼֪ͨ,ʹòѯ״̬ƴ + //HUB˿ϵ,ѯ˿״̬,ʼ豸ӵHUB˿,ʼ豸 + for(i = 1; i <= ThisUsb2Dev.GpHUBPortNum; i++) // HUB˿ڶϵ + { + DevOnU2HubPort[i - 1].DeviceStatus = ROOT_DEV_DISCONNECT; // ⲿHUB˿豸״̬ + s = U2HubSetPortFeature(i, HUB_PORT_POWER); + if(s != ERR_SUCCESS) + { + PRINT("Ext-HUB Port_%1d# power on error\n", (uint16_t)i); // ˿ϵʧ + } + } + PRINT("U2 USB-HUB Ready\n"); + SetUsb2Speed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + } + } + else + { // Խһ + s = CtrlSetUsb2Config(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + // 豣˵ϢԱUSB + ThisUsb2Dev.DeviceStatus = ROOT_DEV_SUCCESS; + ThisUsb2Dev.DeviceType = DEV_TYPE_UNKNOW; + SetUsb2Speed(1); // ĬΪȫ + return (ERR_SUCCESS); /* δ֪豸ʼɹ */ + } + } +#endif + } + } + } + + PRINT("InitRootU2Dev Err = %02X\n", (uint16_t)s); +#ifdef FOR_ROOT_UDISK_ONLY + CHRV3DiskStatus = DISK_CONNECT; +#else + ThisUsb2Dev.DeviceStatus = ROOT_DEV_FAILED; +#endif + SetUsb2Speed(1); // ĬΪȫ + return (s); +} + +/********************************************************************* + * @fn InitU2DevOnHub + * + * @brief ʼöⲿHUBĶUSB豸 + * + * @param HubPortIndex - ָⲿHUB + * + * @return + */ +uint8_t InitU2DevOnHub(uint8_t HubPortIndex) +{ + uint8_t i, s, cfg, dv_cls, if_cls; + uint8_t ifc; + PRINT("Init dev @ExtHub-port_%1d ", (uint16_t)HubPortIndex); + if(HubPortIndex == 0) + { + return (ERR_USB_UNKNOWN); + } + SelectU2HubPort(HubPortIndex); // ѡָROOT-HUB˿ڵⲿHUBָ˿,ѡٶ + PRINT("GetDevDescr: "); + s = CtrlGetU2DeviceDescr(); // ȡ豸 + if(s != ERR_SUCCESS) + { + return (s); + } + DevOnU2HubPort[HubPortIndex - 1].DeviceVID = ((uint16_t)((PUSB_DEV_DESCR)U2Com_Buffer)->idVendor); //VID PIDϢ + DevOnU2HubPort[HubPortIndex - 1].DevicePID = ((uint16_t)((PUSB_DEV_DESCR)U2Com_Buffer)->idProduct); + + dv_cls = ((PUSB_DEV_DESCR)U2Com_Buffer)->bDeviceClass; // 豸 + cfg = (1 << 4) + HubPortIndex; // һUSBַ,ַص + s = CtrlSetUsb2Address(cfg); // USB豸ַ + if(s != ERR_SUCCESS) + { + return (s); + } + DevOnU2HubPort[HubPortIndex - 1].DeviceAddress = cfg; // USBַ + PRINT("GetCfgDescr: "); + s = CtrlGetU2ConfigDescr(); // ȡ + if(s != ERR_SUCCESS) + { + return (s); + } + cfg = ((PUSB_CFG_DESCR)U2Com_Buffer)->bConfigurationValue; + for(i = 0; i < ((PUSB_CFG_DESCR)U2Com_Buffer)->wTotalLength; i++) + { + PRINT("x%02X ", (uint16_t)(U2Com_Buffer[i])); + } + PRINT("\n"); + /* ,ȡ˵/˵ַ/˵С,±endp_addrendp_size */ + if_cls = ((PXUSB_CFG_DESCR_LONG)U2Com_Buffer)->itf_descr.bInterfaceClass; // ӿ + if(dv_cls == 0x00 && if_cls == USB_DEV_CLASS_STORAGE) // USB洢豸,ȷU + { + AnalyzeU2BulkEndp(U2Com_Buffer, HubPortIndex); + for(i = 0; i != 4; i++) + { + PRINT("%02x ", (uint16_t)DevOnU2HubPort[HubPortIndex - 1].GpVar[i]); + } + PRINT("\n"); + s = CtrlSetUsb2Config(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + DevOnU2HubPort[HubPortIndex - 1].DeviceStatus = ROOT_DEV_SUCCESS; + DevOnU2HubPort[HubPortIndex - 1].DeviceType = USB_DEV_CLASS_STORAGE; + PRINT("USB-Disk Ready\n"); + SetUsb2Speed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + } + else if((dv_cls == 0x00) && (if_cls == USB_DEV_CLASS_HID) && (((PXUSB_CFG_DESCR_LONG)U2Com_Buffer)->itf_descr.bInterfaceSubClass <= 0x01)) // HID豸,/ + { + ifc = ((PXUSB_CFG_DESCR_LONG)U2Com_Buffer)->cfg_descr.bNumInterfaces; + s = AnalyzeU2HidIntEndp(U2Com_Buffer, HubPortIndex); // зHIDж϶˵ĵַ + PRINT("AnalyzeU2HidIntEndp %02x\n", (uint16_t)s); + if_cls = ((PXUSB_CFG_DESCR_LONG)U2Com_Buffer)->itf_descr.bInterfaceProtocol; + s = CtrlSetUsb2Config(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + for(dv_cls = 0; dv_cls < ifc; dv_cls++) + { + s = CtrlGetU2HIDDeviceReport(dv_cls); //ȡ + if(s == ERR_SUCCESS) + { + for(i = 0; i < 64; i++) + { + PRINT("x%02X ", (uint16_t)(U2Com_Buffer[i])); + } + PRINT("\n"); + } + } + //豣˵ϢԱUSB + DevOnU2HubPort[HubPortIndex - 1].DeviceStatus = ROOT_DEV_SUCCESS; + if(if_cls == 1) + { + DevOnU2HubPort[HubPortIndex - 1].DeviceType = DEV_TYPE_KEYBOARD; + //һʼ,豸ָʾLED + if(ifc > 1) + { + PRINT("USB_DEV_CLASS_HID Ready\n"); + // DevOnU2HubPort[HubPortIndex - 1].DeviceType = USB_DEV_CLASS_HID; //HID豸 + } + PRINT("USB-Keyboard Ready\n"); + SetUsb2Speed(1); // ĬΪȫ + + return (ERR_SUCCESS); + } + else if(if_cls == 2) + { + DevOnU2HubPort[HubPortIndex - 1].DeviceType = DEV_TYPE_MOUSE; + //ΪԺѯ״̬,Ӧ÷,ȡж϶˿ڵĵַ,ȵϢ + if(ifc > 1) + { + PRINT("USB_DEV_CLASS_HID Ready\n"); + // DevOnU2HubPort[HubPortIndex - 1].DeviceType = USB_DEV_CLASS_HID; //HID豸 + } + PRINT("USB-Mouse Ready\n"); + SetUsb2Speed(1); // ĬΪȫ + + return (ERR_SUCCESS); + } + s = ERR_USB_UNSUPPORT; + } + } + else if(dv_cls == USB_DEV_CLASS_HUB) // HUB豸, + { + DevOnU2HubPort[HubPortIndex - 1].DeviceType = USB_DEV_CLASS_HUB; + PRINT("This program don't support Level 2 HUB\n"); // Ҫֶ֧༶HUBοչ + s = U2HubClearPortFeature(i, HUB_PORT_ENABLE); // ֹHUB˿ + if(s != ERR_SUCCESS) + { + return (s); + } + s = ERR_USB_UNSUPPORT; + } + else //豸 + { + AnalyzeU2BulkEndp(U2Com_Buffer, HubPortIndex); //˵ + for(i = 0; i != 4; i++) + { + PRINT("%02x ", (uint16_t)DevOnU2HubPort[HubPortIndex - 1].GpVar[i]); + } + PRINT("\n"); + s = CtrlSetUsb2Config(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + //豣˵ϢԱUSB + DevOnU2HubPort[HubPortIndex - 1].DeviceStatus = ROOT_DEV_SUCCESS; + DevOnU2HubPort[HubPortIndex - 1].DeviceType = dv_cls ? dv_cls : if_cls; + SetUsb2Speed(1); // ĬΪȫ + return (ERR_SUCCESS); //δ֪豸ʼɹ + } + } + PRINT("InitDevOnHub Err = %02X\n", (uint16_t)s); + DevOnU2HubPort[HubPortIndex - 1].DeviceStatus = ROOT_DEV_FAILED; + SetUsb2Speed(1); // ĬΪȫ + return (s); +} + +/********************************************************************* + * @fn EnumU2HubPort + * + * @brief öָROOT-HUB˿ϵⲿHUBĸ˿,˿ӻƳ¼ʼUSB豸 + * + * @param RootHubIndex - ROOT_HUB0ROOT_HUB1 + * + * @return + */ +uint8_t EnumU2HubPort() +{ + uint8_t i, s; + + for(i = 1; i <= ThisUsb2Dev.GpHUBPortNum; i++) // ѯĶ˿Ƿб仯 + { + SelectU2HubPort(0); // ѡָROOT-HUB˿,õǰUSBٶԼ豸USBַ + s = U2HubGetPortStatus(i); // ȡ˿״̬ + if(s != ERR_SUCCESS) + { + return (s); // ǸHUBϿ + } + if(((U2Com_Buffer[0] & (1 << (HUB_PORT_CONNECTION & 0x07))) && (U2Com_Buffer[2] & (1 << (HUB_C_PORT_CONNECTION & 0x07)))) || (U2Com_Buffer[2] == 0x10)) + { // 豸 + DevOnU2HubPort[i - 1].DeviceStatus = ROOT_DEV_CONNECTED; // 豸 + DevOnU2HubPort[i - 1].DeviceAddress = 0x00; + s = U2HubGetPortStatus(i); // ȡ˿״̬ + if(s != ERR_SUCCESS) + { + return (s); // ǸHUBϿ + } + DevOnU2HubPort[i - 1].DeviceSpeed = U2Com_Buffer[1] & (1 << (HUB_PORT_LOW_SPEED & 0x07)) ? 0 : 1; // ٻȫ + if(DevOnU2HubPort[i - 1].DeviceSpeed) + { + PRINT("Found full speed device on port %1d\n", (uint16_t)i); + } + else + { + PRINT("Found low speed device on port %1d\n", (uint16_t)i); + } + mDelaymS(200); // ȴ豸ϵȶ + s = U2HubSetPortFeature(i, HUB_PORT_RESET); // 豸ӵĶ˿ڸλ + if(s != ERR_SUCCESS) + { + return (s); // ǸHUBϿ + } + PRINT("Reset port and then wait in\n"); + do // ѯλ˿,ֱλ,ɺ״̬ʾ + { + mDelaymS(1); + s = U2HubGetPortStatus(i); + if(s != ERR_SUCCESS) + { + return (s); // ǸHUBϿ + } + } while(U2Com_Buffer[0] & (1 << (HUB_PORT_RESET & 0x07))); // ˿ڸλȴ + mDelaymS(100); + s = U2HubClearPortFeature(i, HUB_C_PORT_RESET); // λɱ־ + // s = U2HubSetPortFeature( i, HUB_PORT_ENABLE ); // HUB˿ + s = U2HubClearPortFeature(i, HUB_C_PORT_CONNECTION); // ӻƳ仯־ + if(s != ERR_SUCCESS) + { + return (s); + } + s = U2HubGetPortStatus(i); // ٶȡ״̬,豸Ƿ + if(s != ERR_SUCCESS) + { + return (s); + } + if((U2Com_Buffer[0] & (1 << (HUB_PORT_CONNECTION & 0x07))) == 0) + { + DevOnU2HubPort[i - 1].DeviceStatus = ROOT_DEV_DISCONNECT; // 豸 + } + s = InitU2DevOnHub(i); // ʼUSB豸 + if(s != ERR_SUCCESS) + { + return (s); + } + SetUsb2Speed(1); // ĬΪȫ + } + else if(U2Com_Buffer[2] & (1 << (HUB_C_PORT_ENABLE & 0x07))) // 豸ӳ + { + U2HubClearPortFeature(i, HUB_C_PORT_ENABLE); // Ӵ־ + PRINT("Device on port error\n"); + s = U2HubSetPortFeature(i, HUB_PORT_RESET); // 豸ӵĶ˿ڸλ + if(s != ERR_SUCCESS) + return (s); // ǸHUBϿ + do // ѯλ˿,ֱλ,ɺ״̬ʾ + { + mDelaymS(1); + s = U2HubGetPortStatus(i); + if(s != ERR_SUCCESS) + return (s); // ǸHUBϿ + } while(U2Com_Buffer[0] & (1 << (HUB_PORT_RESET & 0x07))); // ˿ڸλȴ + } + else if((U2Com_Buffer[0] & (1 << (HUB_PORT_CONNECTION & 0x07))) == 0) // 豸ѾϿ + { + if(DevOnU2HubPort[i - 1].DeviceStatus >= ROOT_DEV_CONNECTED) + { + PRINT("Device on port %1d removed\n", (uint16_t)i); + } + DevOnU2HubPort[i - 1].DeviceStatus = ROOT_DEV_DISCONNECT; // 豸 + if(U2Com_Buffer[2] & (1 << (HUB_C_PORT_CONNECTION & 0x07))) + { + U2HubClearPortFeature(i, HUB_C_PORT_CONNECTION); // Ƴ仯־ + } + } + } + return (ERR_SUCCESS); // زɹ +} + +/********************************************************************* + * @fn EnumAllU2HubPort + * + * @brief öROOT-HUB˿ⲿHUBĶUSB豸 + * + * @return + */ +uint8_t EnumAllU2HubPort(void) +{ + uint8_t s; + + if((ThisUsb2Dev.DeviceStatus >= ROOT_DEV_SUCCESS) && (ThisUsb2Dev.DeviceType == USB_DEV_CLASS_HUB)) // HUBöٳɹ + { + SelectU2HubPort(0); // ѡָROOT-HUB˿,õǰUSBٶԼ豸USBַ + s = EnumU2HubPort(); // öָROOT-HUB˿ϵⲿHUBĸ˿,˿ӻƳ¼ + if(s != ERR_SUCCESS) // HUBϿ + { + PRINT("EnumAllHubPort err = %02X\n", (uint16_t)s); + } + SetUsb2Speed(1); // ĬΪȫ + } + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn U2SearchTypeDevice + * + * @brief ROOT-HUBԼⲿHUB˿ָ͵豸ڵĶ˿ں,˿ںΪ0xFFFFδ. + * ȻҲԸUSBijVIDƷPID(Ҫ¼豸VIDPID),Լָ + * + * @param type - 豸 + * + * @return 8λΪROOT-HUB˿ں,8λΪⲿHUBĶ˿ں,8λΪ0豸ֱROOT-HUB˿ + */ +uint16_t U2SearchTypeDevice(uint8_t type) +{ + uint8_t RootHubIndex; //CH554ֻһUSB,RootHubIndex = 0,ֻ迴ֵĵͰλ + uint8_t HubPortIndex; + + RootHubIndex = 0; + if((ThisUsb2Dev.DeviceType == USB_DEV_CLASS_HUB) && (ThisUsb2Dev.DeviceStatus >= ROOT_DEV_SUCCESS)) // ⲿHUBöٳɹ + { + for(HubPortIndex = 1; HubPortIndex <= ThisUsb2Dev.GpHUBPortNum; HubPortIndex++) // ⲿHUBĸ˿ + { + if(DevOnU2HubPort[HubPortIndex - 1].DeviceType == type && DevOnU2HubPort[HubPortIndex - 1].DeviceStatus >= ROOT_DEV_SUCCESS) + { + return (((uint16_t)RootHubIndex << 8) | HubPortIndex); // ƥöٳɹ + } + } + } + if((ThisUsb2Dev.DeviceType == type) && (ThisUsb2Dev.DeviceStatus >= ROOT_DEV_SUCCESS)) + { + return ((uint16_t)RootHubIndex << 8); // ƥöٳɹ,ROOT-HUB˿ + } + + return (0xFFFF); +} + +/********************************************************************* + * @fn U2SETorOFFNumLock + * + * @brief NumLockĵж + * + * @param buf - Ƽֵ + * + * @return + */ +uint8_t U2SETorOFFNumLock(uint8_t *buf) +{ + uint8_t tmp[] = {0x21, 0x09, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00}; + uint8_t len, s; + if((buf[2] == 0x53) & ((buf[0] | buf[1] | buf[3] | buf[4] | buf[5] | buf[6] | buf[7]) == 0)) + { + for(s = 0; s != sizeof(tmp); s++) + { + ((uint8_t *)pU2SetupReq)[s] = tmp[s]; + } + s = U2HostCtrlTransfer(U2Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + } + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn CtrlGetU2HIDDeviceReport + * + * @brief ȡHID豸,TxBuffer + * + * @param none + * + * @return + */ +uint8_t CtrlGetU2HIDDeviceReport(uint8_t infc) +{ + uint8_t s; + uint8_t len; + + CopyU2SetupReqPkg((uint8_t *)SetupSetU2HIDIdle); + pU2SetupReq->wIndex = infc; + s = U2HostCtrlTransfer(U2Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + + CopyU2SetupReqPkg((uint8_t *)SetupGetU2HIDDevReport); + pU2SetupReq->wIndex = infc; + s = U2HostCtrlTransfer(U2Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn CtrlGetU2HubDescr + * + * @brief ȡHUB,Com_Buffer + * + * @param none + * + * @return + */ +uint8_t CtrlGetU2HubDescr(void) +{ + uint8_t s; + uint8_t len; + + CopyU2SetupReqPkg((uint8_t *)SetupGetU2HubDescr); + s = U2HostCtrlTransfer(U2Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + if(len < ((PUSB_SETUP_REQ)SetupGetU2HubDescr)->wLength) + { + return (ERR_USB_BUF_OVER); // ȴ + } + // if ( len < 4 ) return( ERR_USB_BUF_OVER ); // ȴ + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn U2HubGetPortStatus + * + * @brief ѯHUB˿״̬,Com_Buffer + * + * @param HubPortIndex - ˿ں + * + * @return + */ +uint8_t U2HubGetPortStatus(uint8_t HubPortIndex) +{ + uint8_t s; + uint8_t len; + + pU2SetupReq->bRequestType = HUB_GET_PORT_STATUS; + pU2SetupReq->bRequest = HUB_GET_STATUS; + pU2SetupReq->wValue = 0x0000; + pU2SetupReq->wIndex = 0x0000 | HubPortIndex; + pU2SetupReq->wLength = 0x0004; + s = U2HostCtrlTransfer(U2Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + if(len < 4) + { + return (ERR_USB_BUF_OVER); // ȴ + } + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn U2HubSetPortFeature + * + * @brief HUB˿ + * + * @param HubPortIndex - ˿ں + * @param FeatureSelt - ˿ + * + * @return + */ +uint8_t U2HubSetPortFeature(uint8_t HubPortIndex, uint8_t FeatureSelt) +{ + pU2SetupReq->bRequestType = HUB_SET_PORT_FEATURE; + pU2SetupReq->bRequest = HUB_SET_FEATURE; + pU2SetupReq->wValue = 0x0000 | FeatureSelt; + pU2SetupReq->wIndex = 0x0000 | HubPortIndex; + pU2SetupReq->wLength = 0x0000; + return (U2HostCtrlTransfer(NULL, NULL)); // ִпƴ +} + +/********************************************************************* + * @fn U2HubClearPortFeature + * + * @brief HUB˿ + * + * @param HubPortIndex - ˿ں + * @param FeatureSelt - ˿ + * + * @return + */ +uint8_t U2HubClearPortFeature(uint8_t HubPortIndex, uint8_t FeatureSelt) +{ + pU2SetupReq->bRequestType = HUB_CLEAR_PORT_FEATURE; + pU2SetupReq->bRequest = HUB_CLEAR_FEATURE; + pU2SetupReq->wValue = 0x0000 | FeatureSelt; + pU2SetupReq->wIndex = 0x0000 | HubPortIndex; + pU2SetupReq->wLength = 0x0000; + return (U2HostCtrlTransfer(NULL, NULL)); // ִпƴ +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbhostBase.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbhostBase.c new file mode 100644 index 0000000..27a9d44 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbhostBase.c @@ -0,0 +1,692 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_usbhost.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" +#if DISK_LIB_ENABLE + #include "CHRV3UFI.H" +#endif + +uint8_t UsbDevEndp0Size; // USB豸Ķ˵0ߴ +uint8_t FoundNewDev; +_RootHubDev ThisUsbDev; //ROOT +_DevOnHubPort DevOnHubPort[HUB_MAX_PORTS]; // ٶ:1ⲿHUB,ÿⲿHUBHUB_MAX_PORTS˿(˲) + +uint8_t *pHOST_RX_RAM_Addr; +uint8_t *pHOST_TX_RAM_Addr; + +/*ȡ豸*/ +__attribute__((aligned(4))) const uint8_t SetupGetDevDescr[] = {USB_REQ_TYP_IN, USB_GET_DESCRIPTOR, 0x00, + USB_DESCR_TYP_DEVICE, 0x00, 0x00, sizeof(USB_DEV_DESCR), 0x00}; +/*ȡ*/ +__attribute__((aligned(4))) const uint8_t SetupGetCfgDescr[] = {USB_REQ_TYP_IN, USB_GET_DESCRIPTOR, 0x00, + USB_DESCR_TYP_CONFIG, 0x00, 0x00, 0x04, 0x00}; +/*USBַ*/ +__attribute__((aligned(4))) const uint8_t SetupSetUsbAddr[] = {USB_REQ_TYP_OUT, USB_SET_ADDRESS, USB_DEVICE_ADDR, 0x00, + 0x00, 0x00, 0x00, 0x00}; +/*USB*/ +__attribute__((aligned(4))) const uint8_t SetupSetUsbConfig[] = {USB_REQ_TYP_OUT, USB_SET_CONFIGURATION, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; +/*USBӿ*/ +__attribute__((aligned(4))) const uint8_t SetupSetUsbInterface[] = {USB_REQ_RECIP_INTERF, USB_SET_INTERFACE, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; +/*˵STALL*/ +__attribute__((aligned(4))) const uint8_t SetupClrEndpStall[] = {USB_REQ_TYP_OUT | USB_REQ_RECIP_ENDP, USB_CLEAR_FEATURE, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/********************************************************************* + * @fn DisableRootHubPort + * + * @brief رROOT-HUB˿,ʵӲѾԶر,˴ֻһЩṹ״̬ + * + * @param none + * + * @return none + */ +void DisableRootHubPort(void) +{ +#ifdef FOR_ROOT_UDISK_ONLY + CHRV3DiskStatus = DISK_DISCONNECT; +#endif +#ifndef DISK_BASE_BUF_LEN + ThisUsbDev.DeviceStatus = ROOT_DEV_DISCONNECT; + ThisUsbDev.DeviceAddress = 0x00; +#endif +} + +/********************************************************************* + * @fn AnalyzeRootHub + * + * @brief ROOT-HUB״̬,ROOT-HUB˿ڵ豸¼ + * 豸γ,еDisableRootHubPort(),˿ڹر,¼,Ӧ˿ڵ״̬λ + * + * @param none + * + * @return ERR_SUCCESSΪû,ERR_USB_CONNECTΪ⵽,ERR_USB_DISCONΪ⵽Ͽ + */ +uint8_t AnalyzeRootHub(void) +{ + uint8_t s; + + s = ERR_SUCCESS; + + if(R8_USB_MIS_ST & RB_UMS_DEV_ATTACH) + { // 豸 +#ifdef DISK_BASE_BUF_LEN + if(CHRV3DiskStatus == DISK_DISCONNECT +#else + if(ThisUsbDev.DeviceStatus == ROOT_DEV_DISCONNECT // ⵽豸 +#endif + || (R8_UHOST_CTRL & RB_UH_PORT_EN) == 0x00) + { // ⵽豸,δ,˵Ǹղ + DisableRootHubPort(); // رն˿ +#ifdef DISK_BASE_BUF_LEN + CHRV3DiskStatus = DISK_CONNECT; +#else + ThisUsbDev.DeviceSpeed = R8_USB_MIS_ST & RB_UMS_DM_LEVEL ? 0 : 1; + ThisUsbDev.DeviceStatus = ROOT_DEV_CONNECTED; //ӱ־ +#endif + PRINT("USB dev in\n"); + s = ERR_USB_CONNECT; + } + } + +#ifdef DISK_BASE_BUF_LEN + else if(CHRV3DiskStatus >= DISK_CONNECT) + { +#else + else if(ThisUsbDev.DeviceStatus >= ROOT_DEV_CONNECTED) + { //⵽豸γ +#endif + DisableRootHubPort(); // رն˿ + PRINT("USB dev out\n"); + if(s == ERR_SUCCESS) + { + s = ERR_USB_DISCON; + } + } + // R8_USB_INT_FG = RB_UIF_DETECT; // жϱ־ + return (s); +} + +/********************************************************************* + * @fn SetHostUsbAddr + * + * @brief USBǰUSB豸ַ + * + * @param addr - USB豸ַ + * + * @return none + */ +void SetHostUsbAddr(uint8_t addr) +{ + R8_USB_DEV_AD = (R8_USB_DEV_AD & RB_UDA_GP_BIT) | (addr & MASK_USB_ADDR); +} + +/********************************************************************* + * @fn SetUsbSpeed + * + * @brief õǰUSBٶ + * + * @param FullSpeed - USBٶ + * + * @return none + */ +void SetUsbSpeed(uint8_t FullSpeed) +{ +#ifndef DISK_BASE_BUF_LEN + if(FullSpeed) // ȫ + { + R8_USB_CTRL &= ~RB_UC_LOW_SPEED; // ȫ + R8_UH_SETUP &= ~RB_UH_PRE_PID_EN; // ֹPRE PID + } + else + { + R8_USB_CTRL |= RB_UC_LOW_SPEED; // + } +#endif + (void)FullSpeed; +} + +/********************************************************************* + * @fn ResetRootHubPort + * + * @brief ⵽豸,λ,Ϊö豸׼,ΪĬΪȫ + * + * @param none + * + * @return none + */ +void ResetRootHubPort(void) +{ + UsbDevEndp0Size = DEFAULT_ENDP0_SIZE; //USB豸Ķ˵0ߴ + SetHostUsbAddr(0x00); + R8_UHOST_CTRL &= ~RB_UH_PORT_EN; // ص˿ + SetUsbSpeed(1); // ĬΪȫ + R8_UHOST_CTRL = (R8_UHOST_CTRL & ~RB_UH_LOW_SPEED) | RB_UH_BUS_RESET; // ĬΪȫ,ʼλ + mDelaymS(15); // λʱ10mS20mS + R8_UHOST_CTRL = R8_UHOST_CTRL & ~RB_UH_BUS_RESET; // λ + mDelayuS(250); + R8_USB_INT_FG = RB_UIF_DETECT; // жϱ־ +} + +/********************************************************************* + * @fn EnableRootHubPort + * + * @brief ʹROOT-HUB˿,ӦbUH_PORT_EN1˿,豸Ͽܵ·ʧ + * + * @param none + * + * @return ERR_SUCCESSΪ⵽,ERR_USB_DISCONΪ + */ +uint8_t EnableRootHubPort(void) +{ +#ifdef DISK_BASE_BUF_LEN + if(CHRV3DiskStatus < DISK_CONNECT) + CHRV3DiskStatus = DISK_CONNECT; +#else + if(ThisUsbDev.DeviceStatus < ROOT_DEV_CONNECTED) + ThisUsbDev.DeviceStatus = ROOT_DEV_CONNECTED; +#endif + if(R8_USB_MIS_ST & RB_UMS_DEV_ATTACH) + { // 豸 +#ifndef DISK_BASE_BUF_LEN + if((R8_UHOST_CTRL & RB_UH_PORT_EN) == 0x00) + { // δʹ + ThisUsbDev.DeviceSpeed = (R8_USB_MIS_ST & RB_UMS_DM_LEVEL) ? 0 : 1; + if(ThisUsbDev.DeviceSpeed == 0) + { + R8_UHOST_CTRL |= RB_UH_LOW_SPEED; // + } + } +#endif + R8_UHOST_CTRL |= RB_UH_PORT_EN; //ʹHUB˿ + return (ERR_SUCCESS); + } + return (ERR_USB_DISCON); +} + +#ifndef DISK_BASE_BUF_LEN +/********************************************************************* + * @fn SelectHubPort + * + * @brief ѡҪHUB + * + * @param HubPortIndex - ѡָROOT-HUB˿ڵⲿHUBָ˿ + * + * @return None + */ +void SelectHubPort(uint8_t HubPortIndex) +{ + if(HubPortIndex) // ѡָROOT-HUB˿ڵⲿHUBָ˿ + { + SetHostUsbAddr(DevOnHubPort[HubPortIndex - 1].DeviceAddress); // USBǰUSB豸ַ + SetUsbSpeed(DevOnHubPort[HubPortIndex - 1].DeviceSpeed); // õǰUSBٶ + if(DevOnHubPort[HubPortIndex - 1].DeviceSpeed == 0) // ͨⲿHUBUSB豸ͨѶҪǰID + { + R8_UEP1_CTRL |= RB_UH_PRE_PID_EN; // PRE PID + mDelayuS(100); + } + } + else + { + SetHostUsbAddr(ThisUsbDev.DeviceAddress); // USBǰUSB豸ַ + SetUsbSpeed(ThisUsbDev.DeviceSpeed); // USB豸ٶ + } +} +#endif + +/********************************************************************* + * @fn WaitUSB_Interrupt + * + * @brief ȴUSBж + * + * @param none + * + * @return ERR_SUCCESS ݽջ߷ͳɹ,ERR_USB_UNKNOWN ݽջ߷ʧ + */ +uint8_t WaitUSB_Interrupt(void) +{ + uint16_t i; + for(i = WAIT_USB_TOUT_200US; i != 0 && (R8_USB_INT_FG & RB_UIF_TRANSFER) == 0; i--) + { + ; + } + return ((R8_USB_INT_FG & RB_UIF_TRANSFER) ? ERR_SUCCESS : ERR_USB_UNKNOWN); +} + +/********************************************************************* + * @fn USBHostTransact + * + * @brief ,ĿĶ˵ַ/PID,ͬ־,20uSΪλNAKʱ(0,0xFFFF),0ɹ,ʱ/ + * ӳ,ʵӦ,Ϊṩٶ,ӦöԱӳŻ + * + * @param endp_pid - ƺ͵ַ, 4λtoken_pid, 4λǶ˵ַ + * @param tog - ͬ־ + * @param timeout - ʱʱ + * + * @return ERR_USB_UNKNOWN ʱӲ쳣 + * ERR_USB_DISCON 豸Ͽ + * ERR_USB_CONNECT 豸 + * ERR_SUCCESS + */ +uint8_t USBHostTransact(uint8_t endp_pid, uint8_t tog, uint32_t timeout) +{ + uint8_t TransRetry; + + uint8_t s, r; + uint16_t i; + + R8_UH_RX_CTRL = R8_UH_TX_CTRL = tog; + TransRetry = 0; + + do + { + R8_UH_EP_PID = endp_pid; // ָPIDĿĶ˵ + R8_USB_INT_FG = RB_UIF_TRANSFER; + for(i = WAIT_USB_TOUT_200US; i != 0 && (R8_USB_INT_FG & RB_UIF_TRANSFER) == 0; i--) + { + ; + } + R8_UH_EP_PID = 0x00; // ֹͣUSB + if((R8_USB_INT_FG & RB_UIF_TRANSFER) == 0) + { + return (ERR_USB_UNKNOWN); + } + + if(R8_USB_INT_FG & RB_UIF_DETECT) + { // USB豸¼ + // mDelayuS( 200 ); // ȴ + R8_USB_INT_FG = RB_UIF_DETECT; + s = AnalyzeRootHub(); // ROOT-HUB״̬ + + if(s == ERR_USB_CONNECT) + FoundNewDev = 1; +#ifdef DISK_BASE_BUF_LEN + if(CHRV3DiskStatus == DISK_DISCONNECT) + { + return (ERR_USB_DISCON); + } // USB豸Ͽ¼ + if(CHRV3DiskStatus == DISK_CONNECT) + { + return (ERR_USB_CONNECT); + } // USB豸¼ +#else + if(ThisUsbDev.DeviceStatus == ROOT_DEV_DISCONNECT) + { + return (ERR_USB_DISCON); + } // USB豸Ͽ¼ + if(ThisUsbDev.DeviceStatus == ROOT_DEV_CONNECTED) + { + return (ERR_USB_CONNECT); + } // USB豸¼ +#endif + mDelayuS(200); // ȴ + } + + if(R8_USB_INT_FG & RB_UIF_TRANSFER) // ¼ + { + if(R8_USB_INT_ST & RB_UIS_TOG_OK) + { + return (ERR_SUCCESS); + } + r = R8_USB_INT_ST & MASK_UIS_H_RES; // USB豸Ӧ״̬ + if(r == USB_PID_STALL) + { + return (r | ERR_USB_TRANSFER); + } + if(r == USB_PID_NAK) + { + if(timeout == 0) + { + return (r | ERR_USB_TRANSFER); + } + if(timeout < 0xFFFFFFFF) + { + timeout--; + } + --TransRetry; + } + else + switch(endp_pid >> 4) + { + case USB_PID_SETUP: + case USB_PID_OUT: + if(r) + { + return (r | ERR_USB_TRANSFER); + } // dzʱ/,Ӧ + break; // ʱ + case USB_PID_IN: + if(r == USB_PID_DATA0 || r == USB_PID_DATA1) + { // ͬ趪 + } // ͬ + else if(r) + { + return (r | ERR_USB_TRANSFER); + } // dzʱ/,Ӧ + break; // ʱ + default: + return (ERR_USB_UNKNOWN); // ܵ + break; + } + } + else + { // ж,Ӧ÷ + R8_USB_INT_FG = 0xFF; /* жϱ־ */ + } + mDelayuS(15); + } while(++TransRetry < 3); + return (ERR_USB_TRANSFER); // Ӧʱ +} + +/********************************************************************* + * @fn HostCtrlTransfer + * + * @brief ִпƴ,8ֽpSetupReq,DataBufΪѡշ + * + * @param DataBuf - Ҫպͷ,ôDataBufָЧڴź + * @param RetLen - ʵʳɹշܳȱRetLenָֽڱ + * + * @return ERR_USB_BUF_OVER IN״̬׶γ + * ERR_SUCCESS ݽɹ + */ +uint8_t HostCtrlTransfer(uint8_t *DataBuf, uint8_t *RetLen) +{ + uint16_t RemLen = 0; + uint8_t s, RxLen, RxCnt, TxCnt; + uint8_t *pBuf; + uint8_t *pLen; + + pBuf = DataBuf; + pLen = RetLen; + mDelayuS(200); + if(pLen) + { + *pLen = 0; // ʵʳɹշܳ + } + + R8_UH_TX_LEN = sizeof(USB_SETUP_REQ); + s = USBHostTransact(USB_PID_SETUP << 4 | 0x00, 0x00, 200000 / 20); // SETUP׶,200mSʱ + if(s != ERR_SUCCESS) + { + return (s); + } + R8_UH_RX_CTRL = R8_UH_TX_CTRL = RB_UH_R_TOG | RB_UH_R_AUTO_TOG | RB_UH_T_TOG | RB_UH_T_AUTO_TOG; // ĬDATA1 + R8_UH_TX_LEN = 0x01; // Ĭݹ״̬׶ΪIN + RemLen = pSetupReq->wLength; + if(RemLen && pBuf) // Ҫշ + { + if(pSetupReq->bRequestType & USB_REQ_TYP_IN) // + { + while(RemLen) + { + mDelayuS(200); + s = USBHostTransact(USB_PID_IN << 4 | 0x00, R8_UH_RX_CTRL, 200000 / 20); // IN + if(s != ERR_SUCCESS) + { + return (s); + } + RxLen = R8_USB_RX_LEN < RemLen ? R8_USB_RX_LEN : RemLen; + RemLen -= RxLen; + if(pLen) + { + *pLen += RxLen; // ʵʳɹշܳ + } + for(RxCnt = 0; RxCnt != RxLen; RxCnt++) + { + *pBuf = pHOST_RX_RAM_Addr[RxCnt]; + pBuf++; + } + if(R8_USB_RX_LEN == 0 || (R8_USB_RX_LEN & (UsbDevEndp0Size - 1))) + { + break; // ̰ + } + } + R8_UH_TX_LEN = 0x00; // ״̬׶ΪOUT + } + else // + { + while(RemLen) + { + mDelayuS(200); + R8_UH_TX_LEN = RemLen >= UsbDevEndp0Size ? UsbDevEndp0Size : RemLen; + for(TxCnt = 0; TxCnt != R8_UH_TX_LEN; TxCnt++) + { + pHOST_TX_RAM_Addr[TxCnt] = *pBuf; + pBuf++; + } + s = USBHostTransact(USB_PID_OUT << 4 | 0x00, R8_UH_TX_CTRL, 200000 / 20); // OUT + if(s != ERR_SUCCESS) + { + return (s); + } + RemLen -= R8_UH_TX_LEN; + if(pLen) + { + *pLen += R8_UH_TX_LEN; // ʵʳɹշܳ + } + } + // R8_UH_TX_LEN = 0x01; // ״̬׶ΪIN + } + } + mDelayuS(200); + s = USBHostTransact((R8_UH_TX_LEN ? USB_PID_IN << 4 | 0x00 : USB_PID_OUT << 4 | 0x00), RB_UH_R_TOG | RB_UH_T_TOG, 200000 / 20); // STATUS׶ + if(s != ERR_SUCCESS) + { + return (s); + } + if(R8_UH_TX_LEN == 0) + { + return (ERR_SUCCESS); // ״̬OUT + } + if(R8_USB_RX_LEN == 0) + { + return (ERR_SUCCESS); // ״̬IN,IN״̬ݳ + } + return (ERR_USB_BUF_OVER); // IN״̬׶δ +} + +/********************************************************************* + * @fn CopySetupReqPkg + * + * @brief ƿƴ + * + * @param pReqPkt - ַ + * + * @return none + */ +void CopySetupReqPkg(const uint8_t *pReqPkt) // ƿƴ +{ + uint8_t i; + for(i = 0; i != sizeof(USB_SETUP_REQ); i++) + { + ((uint8_t *)pSetupReq)[i] = *pReqPkt; + pReqPkt++; + } +} + +/********************************************************************* + * @fn CtrlGetDeviceDescr + * + * @brief ȡ豸, pHOST_TX_RAM_Addr + * + * @param none + * + * @return ERR_USB_BUF_OVER ȴ + * ERR_SUCCESS ɹ + */ +uint8_t CtrlGetDeviceDescr(void) +{ + uint8_t s; + uint8_t len; + + UsbDevEndp0Size = DEFAULT_ENDP0_SIZE; + CopySetupReqPkg(SetupGetDevDescr); + s = HostCtrlTransfer(Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + UsbDevEndp0Size = ((PUSB_DEV_DESCR)Com_Buffer)->bMaxPacketSize0; // ˵0,Ǽ򻯴,ӦȻȡǰ8ֽںUsbDevEndp0Sizeټ + if(len < ((PUSB_SETUP_REQ)SetupGetDevDescr)->wLength) + { + return (ERR_USB_BUF_OVER); // ȴ + } + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn CtrlGetConfigDescr + * + * @brief ȡ, pHOST_TX_RAM_Addr + * + * @param none + * + * @return ERR_USB_BUF_OVER ȴ + * ERR_SUCCESS ɹ + */ +uint8_t CtrlGetConfigDescr(void) +{ + uint8_t s; + uint8_t len; + + CopySetupReqPkg(SetupGetCfgDescr); + s = HostCtrlTransfer(Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + if(len < ((PUSB_SETUP_REQ)SetupGetCfgDescr)->wLength) + { + return (ERR_USB_BUF_OVER); // سȴ + } + + len = ((PUSB_CFG_DESCR)Com_Buffer)->wTotalLength; + CopySetupReqPkg(SetupGetCfgDescr); + pSetupReq->wLength = len; // ܳ + s = HostCtrlTransfer(Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + +#ifdef DISK_BASE_BUF_LEN + if(len > 64) + len = 64; + memcpy(TxBuffer, Com_Buffer, len); //U̲ʱҪTxBuffer +#endif + + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn CtrlSetUsbAddress + * + * @brief USB豸ַ + * + * @param addr - 豸ַ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlSetUsbAddress(uint8_t addr) +{ + uint8_t s; + + CopySetupReqPkg(SetupSetUsbAddr); + pSetupReq->wValue = addr; // USB豸ַ + s = HostCtrlTransfer(NULL, NULL); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + SetHostUsbAddr(addr); // USBǰUSB豸ַ + mDelaymS(10); // ȴUSB豸ɲ + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn CtrlSetUsbConfig + * + * @brief USB豸 + * + * @param cfg - ֵ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlSetUsbConfig(uint8_t cfg) +{ + CopySetupReqPkg(SetupSetUsbConfig); + pSetupReq->wValue = cfg; // USB豸 + return (HostCtrlTransfer(NULL, NULL)); // ִпƴ +} + +/********************************************************************* + * @fn CtrlClearEndpStall + * + * @brief ˵STALL + * + * @param endp - ˵ַ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlClearEndpStall(uint8_t endp) +{ + CopySetupReqPkg(SetupClrEndpStall); // ˵Ĵ + pSetupReq->wIndex = endp; // ˵ַ + return (HostCtrlTransfer(NULL, NULL)); // ִпƴ +} + +/********************************************************************* + * @fn CtrlSetUsbIntercace + * + * @brief USB豸ӿ + * + * @param cfg - ֵ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlSetUsbIntercace(uint8_t cfg) +{ + CopySetupReqPkg(SetupSetUsbInterface); + pSetupReq->wValue = cfg; // USB豸 + return (HostCtrlTransfer(NULL, NULL)); // ִпƴ +} + +/********************************************************************* + * @fn USB_HostInit + * + * @brief USBܳʼ + * + * @param none + * + * @return none + */ +void USB_HostInit(void) +{ + R8_USB_CTRL = RB_UC_HOST_MODE; + R8_UHOST_CTRL = 0; + R8_USB_DEV_AD = 0x00; + + R8_UH_EP_MOD = RB_UH_EP_TX_EN | RB_UH_EP_RX_EN; + R16_UH_RX_DMA = (uint16_t)(uint32_t)pHOST_RX_RAM_Addr; + R16_UH_TX_DMA = (uint16_t)(uint32_t)pHOST_TX_RAM_Addr; + + R8_UH_RX_CTRL = 0x00; + R8_UH_TX_CTRL = 0x00; + R8_USB_CTRL = RB_UC_HOST_MODE | RB_UC_INT_BUSY | RB_UC_DMA_EN; + R8_UH_SETUP = RB_UH_SOF_EN; + R8_USB_INT_FG = 0xFF; + DisableRootHubPort(); + R8_USB_INT_EN = RB_UIE_TRANSFER | RB_UIE_DETECT; + + FoundNewDev = 0; +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbhostClass.c b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbhostClass.c new file mode 100644 index 0000000..59e28c6 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/CH58x_usbhostClass.c @@ -0,0 +1,832 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH58x_usbhost.c + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#include "CH58x_common.h" +#if DISK_LIB_ENABLE + #include "CHRV3UFI.H" +#endif + +/* HIDϴ */ +__attribute__((aligned(4))) const uint8_t SetupSetHIDIdle[] = {0x21, HID_SET_IDLE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +/* ȡHID豸 */ +__attribute__((aligned(4))) const uint8_t SetupGetHIDDevReport[] = {0x81, USB_GET_DESCRIPTOR, 0x00, USB_DESCR_TYP_REPORT, + 0x00, 0x00, 0x41, 0x00}; +/* ȡHUB */ +__attribute__((aligned(4))) const uint8_t SetupGetHubDescr[] = {HUB_GET_HUB_DESCRIPTOR, HUB_GET_DESCRIPTOR, 0x00, + USB_DESCR_TYP_HUB, 0x00, 0x00, sizeof(USB_HUB_DESCR), 0x00}; + +__attribute__((aligned(4))) uint8_t Com_Buffer[128]; // ûʱ,öʱڴ,öٽҲͨʱ + +/********************************************************************* + * @fn AnalyzeHidIntEndp + * + * @brief зHIDж϶˵ĵַ,HubPortIndex0浽ROOTHUBǷֵ򱣴浽HUB½ṹ + * + * @param buf - ݻַ HubPortIndex0ʾHUB0ʾⲿHUBµĶ˿ں + * + * @return ˵ + */ +uint8_t AnalyzeHidIntEndp(uint8_t *buf, uint8_t HubPortIndex) +{ + uint8_t i, s, l; + s = 0; + + if(HubPortIndex) + { + memset(DevOnHubPort[HubPortIndex - 1].GpVar, 0, sizeof(DevOnHubPort[HubPortIndex - 1].GpVar)); // + } + else + { + memset(ThisUsbDev.GpVar, 0, sizeof(ThisUsbDev.GpVar)); // + } + + for(i = 0; i < ((PUSB_CFG_DESCR)buf)->wTotalLength; i += l) // ж϶˵,ͽӿ + { + if(((PUSB_ENDP_DESCR)(buf + i))->bDescriptorType == USB_DESCR_TYP_ENDP // Ƕ˵ + && (((PUSB_ENDP_DESCR)(buf + i))->bmAttributes & USB_ENDP_TYPE_MASK) == USB_ENDP_TYPE_INTER // ж϶˵ + && (((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_DIR_MASK)) // IN˵ + { // ж϶˵ĵַ,λ7ͬ־λ,0 + if(HubPortIndex) + { + DevOnHubPort[HubPortIndex - 1].GpVar[s] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; + } + else + { + ThisUsbDev.GpVar[s] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; // ж϶˵ĵַԸҪwMaxPacketSizebInterval + } + PRINT("%02x ", (uint16_t)ThisUsbDev.GpVar[s]); + s++; + if(s >= 4) + { + break; //ֻ4˵ + } + } + l = ((PUSB_ENDP_DESCR)(buf + i))->bLength; // ǰ, + if(l > 16) + { + break; + } + } + PRINT("\n"); + return (s); +} + +/********************************************************************* + * @fn AnalyzeBulkEndp + * + * @brief ˵,GpVar[0]GpVar[1]ϴ˵㡣GpVar[2]GpVar[3]´˵ + * + * @param buf - ݻַ HubPortIndex0ʾHUB0ʾⲿHUBµĶ˿ں + * + * @return 0 + */ +uint8_t AnalyzeBulkEndp(uint8_t *buf, uint8_t HubPortIndex) +{ + uint8_t i, s1, s2, l; + s1 = 0; + s2 = 2; + + if(HubPortIndex) + { + memset(DevOnHubPort[HubPortIndex - 1].GpVar, 0, sizeof(DevOnHubPort[HubPortIndex - 1].GpVar)); // + } + else + { + memset(ThisUsbDev.GpVar, 0, sizeof(ThisUsbDev.GpVar)); // + } + + for(i = 0; i < ((PUSB_CFG_DESCR)buf)->wTotalLength; i += l) // ж϶˵,ͽӿ + { + if((((PUSB_ENDP_DESCR)(buf + i))->bDescriptorType == USB_DESCR_TYP_ENDP) // Ƕ˵ + && ((((PUSB_ENDP_DESCR)(buf + i))->bmAttributes & USB_ENDP_TYPE_MASK) == USB_ENDP_TYPE_BULK)) // ж϶˵ + + { + if(HubPortIndex) + { + if(((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_DIR_MASK) + { + DevOnHubPort[HubPortIndex - 1].GpVar[s1++] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; + } + else + { + DevOnHubPort[HubPortIndex - 1].GpVar[s2++] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; + } + } + else + { + if(((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_DIR_MASK) + { + ThisUsbDev.GpVar[s1++] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; + } + else + { + ThisUsbDev.GpVar[s2++] = ((PUSB_ENDP_DESCR)(buf + i))->bEndpointAddress & USB_ENDP_ADDR_MASK; + } + } + + if(s1 == 2) + { + s1 = 1; + } + if(s2 == 4) + { + s2 = 3; + } + } + l = ((PUSB_ENDP_DESCR)(buf + i))->bLength; // ǰ, + if(l > 16) + { + break; + } + } + return (0); +} + +/********************************************************************* + * @fn InitRootDevice + * + * @brief ʼָROOT-HUB˿ڵUSB豸 + * + * @param none + * + * @return + */ +uint8_t InitRootDevice(void) +{ + uint8_t i, s; + uint8_t cfg, dv_cls, if_cls; + + PRINT("Reset host port\n"); + ResetRootHubPort(); // ⵽豸,λӦ˿ڵUSB + for(i = 0, s = 0; i < 100; i++) + { // ȴUSB豸λ,100mSʱ + mDelaymS(1); + if(EnableRootHubPort() == ERR_SUCCESS) + { // ʹܶ˿ + i = 0; + s++; + if(s > 100) + { + break; // Ѿȶ100mS + } + } + } + if(i) + { // λ豸û + DisableRootHubPort(); + PRINT("Disable host port because of disconnect\n"); + return (ERR_USB_DISCON); + } + SetUsbSpeed(ThisUsbDev.DeviceSpeed); // õǰUSBٶ + + PRINT("GetDevDescr: "); + s = CtrlGetDeviceDescr(); // ȡ豸 + if(s == ERR_SUCCESS) + { + for(i = 0; i < ((PUSB_SETUP_REQ)SetupGetDevDescr)->wLength; i++) + { + PRINT("x%02X ", (uint16_t)(Com_Buffer[i])); + } + PRINT("\n"); + + ThisUsbDev.DeviceVID = ((PUSB_DEV_DESCR)Com_Buffer)->idVendor; //VID PIDϢ + ThisUsbDev.DevicePID = ((PUSB_DEV_DESCR)Com_Buffer)->idProduct; + dv_cls = ((PUSB_DEV_DESCR)Com_Buffer)->bDeviceClass; + + s = CtrlSetUsbAddress(((PUSB_SETUP_REQ)SetupSetUsbAddr)->wValue); + if(s == ERR_SUCCESS) + { + ThisUsbDev.DeviceAddress = ((PUSB_SETUP_REQ)SetupSetUsbAddr)->wValue; // USBַ + + PRINT("GetCfgDescr: "); + s = CtrlGetConfigDescr(); + if(s == ERR_SUCCESS) + { + for(i = 0; i < ((PUSB_CFG_DESCR)Com_Buffer)->wTotalLength; i++) + { + PRINT("x%02X ", (uint16_t)(Com_Buffer[i])); + } + PRINT("\n"); + /* ,ȡ˵/˵ַ/˵С,±endp_addrendp_size */ + cfg = ((PUSB_CFG_DESCR)Com_Buffer)->bConfigurationValue; + if_cls = ((PUSB_CFG_DESCR_LONG)Com_Buffer)->itf_descr.bInterfaceClass; // ӿ + + if((dv_cls == 0x00) && (if_cls == USB_DEV_CLASS_STORAGE)) + { // USB洢豸,ȷU +#ifdef FOR_ROOT_UDISK_ONLY + CHRV3DiskStatus = DISK_USB_ADDR; + return (ERR_SUCCESS); + } + else + { + return (ERR_USB_UNSUPPORT); + } +#else + s = CtrlSetUsbConfig(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + ThisUsbDev.DeviceStatus = ROOT_DEV_SUCCESS; + ThisUsbDev.DeviceType = USB_DEV_CLASS_STORAGE; + PRINT("USB-Disk Ready\n"); + SetUsbSpeed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + } + else if((dv_cls == 0x00) && (if_cls == USB_DEV_CLASS_PRINTER) && ((PUSB_CFG_DESCR_LONG)Com_Buffer)->itf_descr.bInterfaceSubClass == 0x01) + { // Ǵӡ豸 + s = CtrlSetUsbConfig(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + // 豣˵ϢԱUSB + ThisUsbDev.DeviceStatus = ROOT_DEV_SUCCESS; + ThisUsbDev.DeviceType = USB_DEV_CLASS_PRINTER; + PRINT("USB-Print Ready\n"); + SetUsbSpeed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + } + else if((dv_cls == 0x00) && (if_cls == USB_DEV_CLASS_HID) && ((PUSB_CFG_DESCR_LONG)Com_Buffer)->itf_descr.bInterfaceSubClass <= 0x01) + { // HID豸,/ + // зHIDж϶˵ĵַ + s = AnalyzeHidIntEndp(Com_Buffer, 0); // зHIDж϶˵ĵַ + PRINT("AnalyzeHidIntEndp %02x\n", (uint16_t)s); + // ж϶˵ĵַ,λ7ͬ־λ,0 + if_cls = ((PUSB_CFG_DESCR_LONG)Com_Buffer)->itf_descr.bInterfaceProtocol; + s = CtrlSetUsbConfig(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + // Set_Idle( ); + // 豣˵ϢԱUSB + ThisUsbDev.DeviceStatus = ROOT_DEV_SUCCESS; + if(if_cls == 1) + { + ThisUsbDev.DeviceType = DEV_TYPE_KEYBOARD; + // һʼ,豸ָʾLED + PRINT("USB-Keyboard Ready\n"); + SetUsbSpeed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + else if(if_cls == 2) + { + ThisUsbDev.DeviceType = DEV_TYPE_MOUSE; + // ΪԺѯ״̬,Ӧ÷,ȡж϶˿ڵĵַ,ȵϢ + PRINT("USB-Mouse Ready\n"); + SetUsbSpeed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + s = ERR_USB_UNSUPPORT; + } + } + else if(dv_cls == USB_DEV_CLASS_HUB) + { // HUB豸, + s = CtrlGetHubDescr(); + if(s == ERR_SUCCESS) + { + PRINT("Max Port:%02X ", (((PXUSB_HUB_DESCR)Com_Buffer)->bNbrPorts)); + ThisUsbDev.GpHUBPortNum = ((PXUSB_HUB_DESCR)Com_Buffer)->bNbrPorts; // HUBĶ˿ + if(ThisUsbDev.GpHUBPortNum > HUB_MAX_PORTS) + { + ThisUsbDev.GpHUBPortNum = HUB_MAX_PORTS; // ΪṹDevOnHubPortʱΪٶÿHUBHUB_MAX_PORTS˿ + } + s = CtrlSetUsbConfig(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + ThisUsbDev.DeviceStatus = ROOT_DEV_SUCCESS; + ThisUsbDev.DeviceType = USB_DEV_CLASS_HUB; + //豣˵ϢԱUSB,ж϶˵HUB¼֪ͨ,ʹòѯ״̬ƴ + //HUB˿ϵ,ѯ˿״̬,ʼ豸ӵHUB˿,ʼ豸 + for(i = 1; i <= ThisUsbDev.GpHUBPortNum; i++) // HUB˿ڶϵ + { + DevOnHubPort[i - 1].DeviceStatus = ROOT_DEV_DISCONNECT; // ⲿHUB˿豸״̬ + s = HubSetPortFeature(i, HUB_PORT_POWER); + if(s != ERR_SUCCESS) + { + PRINT("Ext-HUB Port_%1d# power on error\n", (uint16_t)i); // ˿ϵʧ + } + } + PRINT("USB-HUB Ready\n"); + SetUsbSpeed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + } + } + else + { // Խһ + s = CtrlSetUsbConfig(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + // 豣˵ϢԱUSB + ThisUsbDev.DeviceStatus = ROOT_DEV_SUCCESS; + ThisUsbDev.DeviceType = DEV_TYPE_UNKNOW; + SetUsbSpeed(1); // ĬΪȫ + return (ERR_SUCCESS); /* δ֪豸ʼɹ */ + } + } +#endif + } + } + } + + PRINT("InitRootDev Err = %02X\n", (uint16_t)s); +#ifdef FOR_ROOT_UDISK_ONLY + CHRV3DiskStatus = DISK_CONNECT; +#else + ThisUsbDev.DeviceStatus = ROOT_DEV_FAILED; +#endif + SetUsbSpeed(1); // ĬΪȫ + return (s); +} + +/********************************************************************* + * @fn InitDevOnHub + * + * @brief ʼöⲿHUBĶUSB豸 + * + * @param HubPortIndex - ָⲿHUB + * + * @return + */ +uint8_t InitDevOnHub(uint8_t HubPortIndex) +{ + uint8_t i, s, cfg, dv_cls, if_cls; + uint8_t ifc; + PRINT("Init dev @ExtHub-port_%1d ", (uint16_t)HubPortIndex); + if(HubPortIndex == 0) + { + return (ERR_USB_UNKNOWN); + } + SelectHubPort(HubPortIndex); // ѡָROOT-HUB˿ڵⲿHUBָ˿,ѡٶ + PRINT("GetDevDescr: "); + s = CtrlGetDeviceDescr(); // ȡ豸 + if(s != ERR_SUCCESS) + { + return (s); + } + DevOnHubPort[HubPortIndex - 1].DeviceVID = ((uint16_t)((PUSB_DEV_DESCR)Com_Buffer)->idVendor); //VID PIDϢ + DevOnHubPort[HubPortIndex - 1].DevicePID = ((uint16_t)((PUSB_DEV_DESCR)Com_Buffer)->idProduct); + + dv_cls = ((PUSB_DEV_DESCR)Com_Buffer)->bDeviceClass; // 豸 + cfg = (1 << 4) + HubPortIndex; // һUSBַ,ַص + s = CtrlSetUsbAddress(cfg); // USB豸ַ + if(s != ERR_SUCCESS) + { + return (s); + } + DevOnHubPort[HubPortIndex - 1].DeviceAddress = cfg; // USBַ + PRINT("GetCfgDescr: "); + s = CtrlGetConfigDescr(); // ȡ + if(s != ERR_SUCCESS) + { + return (s); + } + cfg = ((PUSB_CFG_DESCR)Com_Buffer)->bConfigurationValue; + for(i = 0; i < ((PUSB_CFG_DESCR)Com_Buffer)->wTotalLength; i++) + { + PRINT("x%02X ", (uint16_t)(Com_Buffer[i])); + } + PRINT("\n"); + /* ,ȡ˵/˵ַ/˵С,±endp_addrendp_size */ + if_cls = ((PXUSB_CFG_DESCR_LONG)Com_Buffer)->itf_descr.bInterfaceClass; // ӿ + if(dv_cls == 0x00 && if_cls == USB_DEV_CLASS_STORAGE) // USB洢豸,ȷU + { + AnalyzeBulkEndp(Com_Buffer, HubPortIndex); + for(i = 0; i != 4; i++) + { + PRINT("%02x ", (uint16_t)DevOnHubPort[HubPortIndex - 1].GpVar[i]); + } + PRINT("\n"); + s = CtrlSetUsbConfig(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + DevOnHubPort[HubPortIndex - 1].DeviceStatus = ROOT_DEV_SUCCESS; + DevOnHubPort[HubPortIndex - 1].DeviceType = USB_DEV_CLASS_STORAGE; + PRINT("USB-Disk Ready\n"); + SetUsbSpeed(1); // ĬΪȫ + return (ERR_SUCCESS); + } + } + else if((dv_cls == 0x00) && (if_cls == USB_DEV_CLASS_HID) && (((PXUSB_CFG_DESCR_LONG)Com_Buffer)->itf_descr.bInterfaceSubClass <= 0x01)) // HID豸,/ + { + ifc = ((PXUSB_CFG_DESCR_LONG)Com_Buffer)->cfg_descr.bNumInterfaces; + s = AnalyzeHidIntEndp(Com_Buffer, HubPortIndex); // зHIDж϶˵ĵַ + PRINT("AnalyzeHidIntEndp %02x\n", (uint16_t)s); + if_cls = ((PXUSB_CFG_DESCR_LONG)Com_Buffer)->itf_descr.bInterfaceProtocol; + s = CtrlSetUsbConfig(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + for(dv_cls = 0; dv_cls < ifc; dv_cls++) + { + s = CtrlGetHIDDeviceReport(dv_cls); //ȡ + if(s == ERR_SUCCESS) + { + for(i = 0; i < 64; i++) + { + PRINT("x%02X ", (uint16_t)(Com_Buffer[i])); + } + PRINT("\n"); + } + } + //豣˵ϢԱUSB + DevOnHubPort[HubPortIndex - 1].DeviceStatus = ROOT_DEV_SUCCESS; + if(if_cls == 1) + { + DevOnHubPort[HubPortIndex - 1].DeviceType = DEV_TYPE_KEYBOARD; + //һʼ,豸ָʾLED + if(ifc > 1) + { + PRINT("USB_DEV_CLASS_HID Ready\n"); + DevOnHubPort[HubPortIndex - 1].DeviceType = USB_DEV_CLASS_HID; //HID豸 + } + PRINT("USB-Keyboard Ready\n"); + SetUsbSpeed(1); // ĬΪȫ + + return (ERR_SUCCESS); + } + else if(if_cls == 2) + { + DevOnHubPort[HubPortIndex - 1].DeviceType = DEV_TYPE_MOUSE; + //ΪԺѯ״̬,Ӧ÷,ȡж϶˿ڵĵַ,ȵϢ + if(ifc > 1) + { + PRINT("USB_DEV_CLASS_HID Ready\n"); + DevOnHubPort[HubPortIndex - 1].DeviceType = USB_DEV_CLASS_HID; //HID豸 + } + PRINT("USB-Mouse Ready\n"); + SetUsbSpeed(1); // ĬΪȫ + + return (ERR_SUCCESS); + } + s = ERR_USB_UNSUPPORT; + } + } + else if(dv_cls == USB_DEV_CLASS_HUB) // HUB豸, + { + DevOnHubPort[HubPortIndex - 1].DeviceType = USB_DEV_CLASS_HUB; + PRINT("This program don't support Level 2 HUB\n"); // Ҫֶ֧༶HUBοչ + s = HubClearPortFeature(i, HUB_PORT_ENABLE); // ֹHUB˿ + if(s != ERR_SUCCESS) + { + return (s); + } + s = ERR_USB_UNSUPPORT; + } + else //豸 + { + AnalyzeBulkEndp(Com_Buffer, HubPortIndex); //˵ + for(i = 0; i != 4; i++) + { + PRINT("%02x ", (uint16_t)DevOnHubPort[HubPortIndex - 1].GpVar[i]); + } + PRINT("\n"); + s = CtrlSetUsbConfig(cfg); // USB豸 + if(s == ERR_SUCCESS) + { + //豣˵ϢԱUSB + DevOnHubPort[HubPortIndex - 1].DeviceStatus = ROOT_DEV_SUCCESS; + DevOnHubPort[HubPortIndex - 1].DeviceType = dv_cls ? dv_cls : if_cls; + SetUsbSpeed(1); // ĬΪȫ + return (ERR_SUCCESS); //δ֪豸ʼɹ + } + } + PRINT("InitDevOnHub Err = %02X\n", (uint16_t)s); + DevOnHubPort[HubPortIndex - 1].DeviceStatus = ROOT_DEV_FAILED; + SetUsbSpeed(1); // ĬΪȫ + return (s); +} + +/********************************************************************* + * @fn EnumHubPort + * + * @brief öָROOT-HUB˿ϵⲿHUBĸ˿,˿ӻƳ¼ʼUSB豸 + * + * @param RootHubIndex - ROOT_HUB0ROOT_HUB1 + * + * @return + */ +uint8_t EnumHubPort() +{ + uint8_t i, s; + + for(i = 1; i <= ThisUsbDev.GpHUBPortNum; i++) // ѯĶ˿Ƿб仯 + { + SelectHubPort(0); // ѡָROOT-HUB˿,õǰUSBٶԼ豸USBַ + s = HubGetPortStatus(i); // ȡ˿״̬ + if(s != ERR_SUCCESS) + { + return (s); // ǸHUBϿ + } + if(((Com_Buffer[0] & (1 << (HUB_PORT_CONNECTION & 0x07))) && (Com_Buffer[2] & (1 << (HUB_C_PORT_CONNECTION & 0x07)))) || (Com_Buffer[2] == 0x10)) + { // 豸 + DevOnHubPort[i - 1].DeviceStatus = ROOT_DEV_CONNECTED; // 豸 + DevOnHubPort[i - 1].DeviceAddress = 0x00; + s = HubGetPortStatus(i); // ȡ˿״̬ + if(s != ERR_SUCCESS) + { + return (s); // ǸHUBϿ + } + DevOnHubPort[i - 1].DeviceSpeed = Com_Buffer[1] & (1 << (HUB_PORT_LOW_SPEED & 0x07)) ? 0 : 1; // ٻȫ + if(DevOnHubPort[i - 1].DeviceSpeed) + { + PRINT("Found full speed device on port %1d\n", (uint16_t)i); + } + else + { + PRINT("Found low speed device on port %1d\n", (uint16_t)i); + } + mDelaymS(200); // ȴ豸ϵȶ + s = HubSetPortFeature(i, HUB_PORT_RESET); // 豸ӵĶ˿ڸλ + if(s != ERR_SUCCESS) + { + return (s); // ǸHUBϿ + } + PRINT("Reset port and then wait in\n"); + do // ѯλ˿,ֱλ,ɺ״̬ʾ + { + mDelaymS(1); + s = HubGetPortStatus(i); + if(s != ERR_SUCCESS) + { + return (s); // ǸHUBϿ + } + } while(Com_Buffer[0] & (1 << (HUB_PORT_RESET & 0x07))); // ˿ڸλȴ + mDelaymS(100); + s = HubClearPortFeature(i, HUB_C_PORT_RESET); // λɱ־ + // s = HubSetPortFeature( i, HUB_PORT_ENABLE ); // HUB˿ + s = HubClearPortFeature(i, HUB_C_PORT_CONNECTION); // ӻƳ仯־ + if(s != ERR_SUCCESS) + { + return (s); + } + s = HubGetPortStatus(i); // ٶȡ״̬,豸Ƿ + if(s != ERR_SUCCESS) + { + return (s); + } + if((Com_Buffer[0] & (1 << (HUB_PORT_CONNECTION & 0x07))) == 0) + { + DevOnHubPort[i - 1].DeviceStatus = ROOT_DEV_DISCONNECT; // 豸 + } + s = InitDevOnHub(i); // ʼUSB豸 + if(s != ERR_SUCCESS) + { + return (s); + } + SetUsbSpeed(1); // ĬΪȫ + } + else if(Com_Buffer[2] & (1 << (HUB_C_PORT_ENABLE & 0x07))) // 豸ӳ + { + HubClearPortFeature(i, HUB_C_PORT_ENABLE); // Ӵ־ + PRINT("Device on port error\n"); + s = HubSetPortFeature(i, HUB_PORT_RESET); // 豸ӵĶ˿ڸλ + if(s != ERR_SUCCESS) + return (s); // ǸHUBϿ + do // ѯλ˿,ֱλ,ɺ״̬ʾ + { + mDelaymS(1); + s = HubGetPortStatus(i); + if(s != ERR_SUCCESS) + return (s); // ǸHUBϿ + } while(Com_Buffer[0] & (1 << (HUB_PORT_RESET & 0x07))); // ˿ڸλȴ + } + else if((Com_Buffer[0] & (1 << (HUB_PORT_CONNECTION & 0x07))) == 0) // 豸ѾϿ + { + if(DevOnHubPort[i - 1].DeviceStatus >= ROOT_DEV_CONNECTED) + { + PRINT("Device on port %1d removed\n", (uint16_t)i); + } + DevOnHubPort[i - 1].DeviceStatus = ROOT_DEV_DISCONNECT; // 豸 + if(Com_Buffer[2] & (1 << (HUB_C_PORT_CONNECTION & 0x07))) + { + HubClearPortFeature(i, HUB_C_PORT_CONNECTION); // Ƴ仯־ + } + } + } + return (ERR_SUCCESS); // زɹ +} + +/********************************************************************* + * @fn EnumAllHubPort + * + * @brief öROOT-HUB˿ⲿHUBĶUSB豸 + * + * @return + */ +uint8_t EnumAllHubPort(void) +{ + uint8_t s; + + if((ThisUsbDev.DeviceStatus >= ROOT_DEV_SUCCESS) && (ThisUsbDev.DeviceType == USB_DEV_CLASS_HUB)) // HUBöٳɹ + { + SelectHubPort(0); // ѡָROOT-HUB˿,õǰUSBٶԼ豸USBַ + s = EnumHubPort(); // öָROOT-HUB˿ϵⲿHUBĸ˿,˿ӻƳ¼ + if(s != ERR_SUCCESS) // HUBϿ + { + PRINT("EnumAllHubPort err = %02X\n", (uint16_t)s); + } + SetUsbSpeed(1); // ĬΪȫ + } + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn SearchTypeDevice + * + * @brief ROOT-HUBԼⲿHUB˿ָ͵豸ڵĶ˿ں,˿ںΪ0xFFFFδ. + * ȻҲԸUSBijVIDƷPID(Ҫ¼豸VIDPID),Լָ + * + * @param type - 豸 + * + * @return 8λΪROOT-HUB˿ں,8λΪⲿHUBĶ˿ں,8λΪ0豸ֱROOT-HUB˿ + */ +uint16_t SearchTypeDevice(uint8_t type) +{ + uint8_t RootHubIndex; //CH554ֻһUSB,RootHubIndex = 0,ֻ迴ֵĵͰλ + uint8_t HubPortIndex; + + RootHubIndex = 0; + if((ThisUsbDev.DeviceType == USB_DEV_CLASS_HUB) && (ThisUsbDev.DeviceStatus >= ROOT_DEV_SUCCESS)) // ⲿHUBöٳɹ + { + for(HubPortIndex = 1; HubPortIndex <= ThisUsbDev.GpHUBPortNum; HubPortIndex++) // ⲿHUBĸ˿ + { + if(DevOnHubPort[HubPortIndex - 1].DeviceType == type && DevOnHubPort[HubPortIndex - 1].DeviceStatus >= ROOT_DEV_SUCCESS) + { + return (((uint16_t)RootHubIndex << 8) | HubPortIndex); // ƥöٳɹ + } + } + } + if((ThisUsbDev.DeviceType == type) && (ThisUsbDev.DeviceStatus >= ROOT_DEV_SUCCESS)) + { + return ((uint16_t)RootHubIndex << 8); // ƥöٳɹ,ROOT-HUB˿ + } + + return (0xFFFF); +} + +/********************************************************************* + * @fn SETorOFFNumLock + * + * @brief NumLockĵж + * + * @param buf - Ƽֵ + * + * @return + */ +uint8_t SETorOFFNumLock(uint8_t *buf) +{ + uint8_t tmp[] = {0x21, 0x09, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00}; + uint8_t len, s; + if((buf[2] == 0x53) & ((buf[0] | buf[1] | buf[3] | buf[4] | buf[5] | buf[6] | buf[7]) == 0)) + { + for(s = 0; s != sizeof(tmp); s++) + { + ((uint8_t *)pSetupReq)[s] = tmp[s]; + } + s = HostCtrlTransfer(Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + } + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn CtrlGetHIDDeviceReport + * + * @brief ȡHID豸,TxBuffer + * + * @param none + * + * @return + */ +uint8_t CtrlGetHIDDeviceReport(uint8_t infc) +{ + uint8_t s; + uint8_t len; + + CopySetupReqPkg(SetupSetHIDIdle); + pSetupReq->wIndex = infc; + s = HostCtrlTransfer(Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + + CopySetupReqPkg(SetupGetHIDDevReport); + pSetupReq->wIndex = infc; + s = HostCtrlTransfer(Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn CtrlGetHubDescr + * + * @brief ȡHUB,Com_Buffer + * + * @param none + * + * @return + */ +uint8_t CtrlGetHubDescr(void) +{ + uint8_t s; + uint8_t len; + + CopySetupReqPkg(SetupGetHubDescr); + s = HostCtrlTransfer(Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + if(len < ((PUSB_SETUP_REQ)SetupGetHubDescr)->wLength) + { + return (ERR_USB_BUF_OVER); // ȴ + } + // if ( len < 4 ) return( ERR_USB_BUF_OVER ); // ȴ + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn HubGetPortStatus + * + * @brief ѯHUB˿״̬,Com_Buffer + * + * @param HubPortIndex - ˿ں + * + * @return + */ +uint8_t HubGetPortStatus(uint8_t HubPortIndex) +{ + uint8_t s; + uint8_t len; + + pSetupReq->bRequestType = HUB_GET_PORT_STATUS; + pSetupReq->bRequest = HUB_GET_STATUS; + pSetupReq->wValue = 0x0000; + pSetupReq->wIndex = 0x0000 | HubPortIndex; + pSetupReq->wLength = 0x0004; + s = HostCtrlTransfer(Com_Buffer, &len); // ִпƴ + if(s != ERR_SUCCESS) + { + return (s); + } + if(len < 4) + { + return (ERR_USB_BUF_OVER); // ȴ + } + return (ERR_SUCCESS); +} + +/********************************************************************* + * @fn HubSetPortFeature + * + * @brief HUB˿ + * + * @param HubPortIndex - ˿ں + * @param FeatureSelt - ˿ + * + * @return + */ +uint8_t HubSetPortFeature(uint8_t HubPortIndex, uint8_t FeatureSelt) +{ + pSetupReq->bRequestType = HUB_SET_PORT_FEATURE; + pSetupReq->bRequest = HUB_SET_FEATURE; + pSetupReq->wValue = 0x0000 | FeatureSelt; + pSetupReq->wIndex = 0x0000 | HubPortIndex; + pSetupReq->wLength = 0x0000; + return (HostCtrlTransfer(NULL, NULL)); // ִпƴ +} + +/********************************************************************* + * @fn HubClearPortFeature + * + * @brief HUB˿ + * + * @param HubPortIndex - ˿ں + * @param FeatureSelt - ˿ + * + * @return + */ +uint8_t HubClearPortFeature(uint8_t HubPortIndex, uint8_t FeatureSelt) +{ + pSetupReq->bRequestType = HUB_CLEAR_PORT_FEATURE; + pSetupReq->bRequest = HUB_CLEAR_FEATURE; + pSetupReq->wValue = 0x0000 | FeatureSelt; + pSetupReq->wIndex = 0x0000 | HubPortIndex; + pSetupReq->wLength = 0x0000; + return (HostCtrlTransfer(NULL, NULL)); // ִпƴ +} diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH583SFR.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH583SFR.h new file mode 100644 index 0000000..05ee89d --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH583SFR.h @@ -0,0 +1,1898 @@ +/* Define for CH583 */ +/* Website: http://wch.cn */ +/* Email: tech@wch.cn */ +/* Author: W.ch 2020.05 */ +/* V0.2 SpecialFunctionRegister */ + +// multi-blocks: __BASE_TYPE__, __CH583SFR_H__, __CH583USBSFR_H__, __USB_TYPE__... + +#ifndef __BASE_TYPE__ +#define __BASE_TYPE__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ********************************************************************************************************************* */ +/* Base types & constants */ + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif +#ifndef NULL +#define NULL 0 +#endif + +#ifndef VOID +#define VOID void +#endif +#ifndef CONST +#define CONST const +#endif +#ifndef BOOL +typedef unsigned char BOOL; +#endif +#ifndef BOOLEAN +typedef unsigned char BOOLEAN; +#endif +#ifndef CHAR +typedef char CHAR; +#endif +#ifndef INT8 +typedef char INT8; +#endif +#ifndef INT16 +typedef short INT16; +#endif +#ifndef INT32 +typedef long INT32; +#endif +#ifndef UINT8 +typedef unsigned char UINT8; +#endif +#ifndef UINT16 +typedef unsigned short UINT16; +#endif +#ifndef UINT32 +typedef unsigned long UINT32; +#endif +#ifndef UINT64 +typedef unsigned long long UINT64; +#endif +#ifndef UINT8V +typedef unsigned char volatile UINT8V; +#endif +#ifndef UINT16V +typedef unsigned short volatile UINT16V; +#endif +#ifndef UINT32V +typedef unsigned long volatile UINT32V; +#endif +#ifndef UINT64V +typedef unsigned long long volatile UINT64V; +#endif + +#ifndef PVOID +typedef void *PVOID; +#endif +#ifndef PCHAR +typedef char *PCHAR; +#endif +#ifndef PCHAR +typedef const char *PCCHAR; +#endif +#ifndef PINT8 +typedef char *PINT8; +#endif +#ifndef PINT16 +typedef short *PINT16; +#endif +#ifndef PINT32 +typedef long *PINT32; +#endif +#ifndef PUINT8 +typedef unsigned char *PUINT8; +#endif +#ifndef PUINT16 +typedef unsigned short *PUINT16; +#endif +#ifndef PUINT32 +typedef unsigned long *PUINT32; +#endif +#ifndef PUINT8V +typedef volatile unsigned char *PUINT8V; +#endif +#ifndef PUINT16V +typedef volatile unsigned short *PUINT16V; +#endif +#ifndef PUINT32V +typedef volatile unsigned long *PUINT32V; +#endif +#ifndef PUINT64V +typedef volatile unsigned long long *PUINT64V; +#endif + +/* ********************************************************************************************************************* */ +/* Base macros */ + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +/* Calculate the byte offset of a field in a structure of type */ +#define FIELD_OFFSET(Type, Field) ((UINT16)&(((Type *)0)->Field)) + +/* Calculate the size of a field in a structure of type */ +#define FIELD_SIZE(Type, Field) (sizeof(((Type *)0)->Field)) + +/* An expression that yields the type of a field in a struct */ +#define FIELD_TYPE(Type, Field) (((Type *)0)->Field) + +/* Return the number of elements in a statically sized array */ +#define NUMBER_OF(Array) (sizeof(Array)/sizeof((Array)[0])) +#define NUMBER_OF_FIELD(Type, Field) (NUMBER_OF(FIELD_TYPE(Type, Field))) + +#ifdef __cplusplus +} +#endif + +#endif // __BASE_TYPE__ + + +#ifndef __CH583SFR_H__ +#define __CH583SFR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ********************************************************************************************************************* */ + +// Address Space +// CODE: 00000000H - 0007FFFFH 512K +// DATA: 20000000H - 20007FFFH 32KB +// SFR: 40000000H - 4000FFFFH 64KB +// +// SFR: 40000000H - 4000FFFFH, 64KB +// SYS: +1000H - 1BFFH, include base configuration, interrupt, GPIO, etc... +// TMR0: +2000H - 23FFH +// TMR1: +2400H - 27FFH +// TMR2: +2800H - 2BFFH +// TMR3: +2C00H - 2FFFH +// UART0: +3000H - 33FFH +// UART1: +3400H - 37FFH +// UART2: +3800H - 3BFFH +// UART3: +3C00H - 3FFFH +// SPI0: +4000H - 43FFH +// SPI1: +4400H - 47FFH +// I2C: +4800H - 4BFFH +// PWMx: +5000H - 53FFH +// USB: +8000H - 83FFH +// USB2: +8400H - 87FFH +// BLE: +C000H - D3FFH + +// Register Bit Attribute / Bit Access Type +// RF: Read only for Fixed value +// RO: Read Only (internal change) +// RZ: Read only with auto clear Zero +// WO: Write Only (read zero or different) +// WA: Write only under safe Accessing mode (read zero or different) +// WZ: Write only with auto clear Zero +// RW: Read / Write +// RWA: Read / Write under safe Accessing mode +// RW1: Read / Write 1 to Clear + +/* Register name rule: + R32_* for 32 bits register (UINT32,ULONG) + R16_* for 16 bits register (UINT16,USHORT) + R8_* for 8 bits register (UINT8,UCHAR) + RB_* for bit or bit mask of 8 bit register + BA_* for base address point + b* for GPIO bit mask + Others for register address offset */ + +/* ********************************************************************************************************************* */ + +/* System: safe accessing register */ +#define R32_SAFE_ACCESS (*((PUINT32V)0x40001040)) // RW, safe accessing +#define R8_SAFE_ACCESS_SIG (*((PUINT8V)0x40001040)) // WO, safe accessing sign register, must write SAFE_ACCESS_SIG1 then SAFE_ACCESS_SIG2 to enter safe accessing mode +#define RB_SAFE_ACC_MODE 0x03 // RO, current safe accessing mode: 11=safe/unlocked (SAM), other=locked (00..01..10..11) +#define RB_SAFE_ACC_ACT 0x08 // RO, indicate safe accessing status now: 0=locked, read only, 1=safe/unlocked (SAM), write enabled +#define RB_SAFE_ACC_TIMER 0x70 // RO, safe accessing timer bit mask (16*clock number) +#define SAFE_ACCESS_SIG1 0x57 // WO: safe accessing sign value step 1 +#define SAFE_ACCESS_SIG2 0xA8 // WO: safe accessing sign value step 2 +#define SAFE_ACCESS_SIG0 0x00 // WO: safe accessing sign value for disable +#define R8_CHIP_ID (*((PUINT8V)0x40001041)) // RF, chip ID register, always is ID_CH58* +#define R8_SAFE_ACCESS_ID (*((PUINT8V)0x40001042)) // RF, safe accessing ID register, always 0x0C +#define R8_WDOG_COUNT (*((PUINT8V)0x40001043)) // RW, watch-dog count, count by clock frequency Fsys/131072 + +/* System: global configuration register */ +#define R32_GLOBAL_CONFIG (*((PUINT32V)0x40001044)) // RW, global configuration +#define R8_RESET_STATUS (*((PUINT8V)0x40001044)) // RO, reset status +#define RB_RESET_FLAG 0x07 // RO: recent reset flag +#define RST_FLAG_SW 0x00 +#define RST_FLAG_RPOR 0x01 +#define RST_FLAG_WTR 0x02 +#define RST_FLAG_MR 0x03 +//#define RST_FLAG_GPWSM 0x04 // RO, power on reset flag during sleep/shutdown: 0=no power on reset during sleep/shutdown, 1=power on reset occurred during sleep/shutdown +#define RST_FLAG_GPWSM 0x05 +// RB_RESET_FLAG: recent reset flag +// 000 - SR, software reset, by RB_SOFTWARE_RESET=1 @RB_WDOG_RST_EN=0 +// 001 - RPOR, real power on reset +// 010 - WTR, watch-dog timer-out reset +// 011 - MR, external manual reset by RST pin input low +// 101 - GRWSM, global reset by waking under shutdown mode +// 1?? - LRW, power on reset occurred during sleep +#define R8_GLOB_ROM_CFG R8_RESET_STATUS // RWA, flash ROM configuration, SAM +#define RB_ROM_CODE_OFS 0x10 // RWA, code offset address selection in Flash ROM: 0=start address 0x000000, 1=start address 0x040000 +#define RB_ROM_CTRL_EN 0x20 // RWA, enable flash ROM control interface enable: 0=disable access, 1=enable access control register +#define RB_ROM_DATA_WE 0x40 // RWA, enable flash ROM data & code area being erase/write: 0=all writing protect, 1=enable data area program and erase +#define RB_ROM_CODE_WE 0x80 // RWA, enable flash ROM code area being erase/write: 0=code writing protect, 1=enable code area program and erase +#define R8_GLOB_CFG_INFO (*((PUINT8V)0x40001045)) // RO, global configuration information and status +#define RB_CFG_ROM_READ 0x01 // RO, indicate protected status of Flash ROM code and data: 0=reading protect, 1=enable read by external programmer +#define RB_CFG_RESET_EN 0x04 // RO, manual reset input enable status +#define RB_CFG_BOOT_EN 0x08 // RO, boot-loader enable status +#define RB_CFG_DEBUG_EN 0x10 // RO, debug enable status +#define RB_BOOT_LOADER 0x20 // RO, indicate boot loader status: 0=application status (by software reset), 1=boot loader status +#define R8_RST_WDOG_CTRL (*((PUINT8V)0x40001046)) // RWA, reset and watch-dog control, SAM +#define RB_SOFTWARE_RESET 0x01 // WA/WZ, global software reset, high action, auto clear +#define RB_WDOG_RST_EN 0x02 // RWA, enable watch-dog reset if watch-dog timer overflow: 0=as timer only, 1=enable reset if timer overflow +#define RB_WDOG_INT_EN 0x04 // RWA, watch-dog timer overflow interrupt enable: 0=disable, 1=enable +#define RB_WDOG_INT_FLAG 0x10 // RW1, watch-dog timer overflow interrupt flag, cleared by RW1 or reload watch-dog count or __SEV(Send-Event) +#define R8_GLOB_RESET_KEEP (*((PUINT8V)0x40001047)) // RW, value keeper during global reset + +/* System: clock configuration register */ +#define R32_CLOCK_CONFIG (*((PUINT32V)0x40001008)) // RWA, clock configuration, SAM +#define R16_CLK_SYS_CFG (*((PUINT16V)0x40001008)) // RWA, system clock configuration, SAM +#define RB_CLK_PLL_DIV 0x1F // RWA, output clock divider from PLL or CK32M +#define RB_CLK_SYS_MOD 0xC0 // RWA, system clock source mode: 00=divided from 32MHz, 01=divided from PLL-480MHz, 10=directly from 32MHz, 11=directly from 32KHz +#define R8_HFCK_PWR_CTRL (*((PUINT8V)0x4000100A)) // RWA, high frequency clock module power control, SAM +#define RB_CLK_XT32M_PON 0x04 // RWA, external 32MHz oscillator power control: 0=power down, 1-power on +#define RB_CLK_XT32M_KEEP 0x08 // RWA, external 32MHz oscillator power keep under halt mode: 0=auto stop, 1=keep running +#define RB_CLK_PLL_PON 0x10 // RWA, PLL power control: 0=power down, 1-power on +// Fck32k = RB_CLK_OSC32K_XT ? XT_32KHz : RC_32KHz +// Fpll = XT_32MHz * 15 = 480MHz +// Fsys = RB_CLK_SYS_MOD==3 ? Fck32k : ( ( RB_CLK_SYS_MOD[0] ? Fpll : XT_32MHz ) / RB_CLK_PLL_DIV ) +// default: Fsys = XT_32MHz / RB_CLK_PLL_DIV = 32MHz / 5 = 6.4MHz +// range: 32KHz, 2MHz~10MHz, 15MHz~80MHz + +/* System: sleep control register */ +#define R32_SLEEP_CONTROL (*((PUINT32V)0x4000100C)) // RWA, sleep control, SAM +#define R8_SLP_CLK_OFF0 (*((PUINT8V)0x4000100C)) // RWA, sleep clock off control byte 0, SAM +#define RB_SLP_CLK_TMR0 0x01 // RWA, close TMR0 clock +#define RB_SLP_CLK_TMR1 0x02 // RWA, close TMR1 clock +#define RB_SLP_CLK_TMR2 0x04 // RWA, close TMR2 clock +#define RB_SLP_CLK_TMR3 0x08 // RWA, close TMR3 clock +#define RB_SLP_CLK_UART0 0x10 // RWA, close UART0 clock +#define RB_SLP_CLK_UART1 0x20 // RWA, close UART1 clock +#define RB_SLP_CLK_UART2 0x40 // RWA, close UART2 clock +#define RB_SLP_CLK_UART3 0x80 // RWA, close UART3 clock +#define R8_SLP_CLK_OFF1 (*((PUINT8V)0x4000100D)) // RWA, sleep clock off control byte 1, SAM +#define RB_SLP_CLK_SPI0 0x01 // RWA, close SPI0 clock +#define RB_SLP_CLK_SPI1 0x02 // RWA, close SPI1 clock +#define RB_SLP_CLK_PWMX 0x04 // RWA, close PWMx clock +#define RB_SLP_CLK_I2C 0x08 // RWA, close I2C clock +#define RB_SLP_CLK_USB 0x10 // RWA, close USB clock +#define RB_SLP_CLK_USB2 0x20 // RWA, close USB2 clock +#define RB_SLP_CLK_BLE 0x80 // RWA, close BLE clock +#define R8_SLP_WAKE_CTRL (*((PUINT8V)0x4000100E)) // RWA, wake control, SAM +#define RB_SLP_USB_WAKE 0x01 // RWA, enable USB waking +#define RB_SLP_USB2_WAKE 0x02 // RWA, enable USB2 waking +//#define RB_SLP_BLE_WAKE 0x04 // RWA, enable BLE waking +#define RB_SLP_RTC_WAKE 0x08 // RWA, enable RTC waking +#define RB_SLP_GPIO_WAKE 0x10 // RWA, enable GPIO waking +#define RB_SLP_BAT_WAKE 0x20 // RWA, enable BAT waking +#define RB_WAKE_EV_MODE 0x40 // RWA, event wakeup mode: 0=event keep valid for long time, 1=short pulse event +#define R8_SLP_POWER_CTRL (*((PUINT8V)0x4000100F)) // RWA, peripherals power down control, SAM +#define RB_WAKE_DLY_MOD 0x03 // RWA, wakeup delay time selection +// RB_WAKE_DLY_MOD select wakeup delay +// 00: long time, 3590 cycles+TSUHSE +// 01: short time, 520 cycles+TSUHSE +// 10: shorter time, 70 cycles+TSUHSE +// 11: no delay, 8 cycles+TSUHSE +//#define RB_SLP_USB_PWR_DN 0x01 // RWA, enable USB power down +//#define RB_SLP_BLE_PWR_DN 0x04 // RWA, enable BLE power down +#define RB_SLP_CLK_RAMX 0x10 // RWA, close main SRAM clock +#define RB_SLP_CLK_RAM2K 0x20 // RWA, close retention 2KB SRAM clock +#define RB_RAM_RET_LV 0x40 // RWA, SRAM retention voltage selection: 0=normal, 1=low voltage for low power + +/* System: I/O pin configuration register */ +#define R32_PIN_CONFIG (*((PUINT32V)0x40001018)) // RW, I/O pin configuration +#define R16_PIN_ALTERNATE (*((PUINT16V)0x40001018)) // RW, function pin alternate configuration +#define RB_PIN_TMR0 0x01 // RW, TMR0 alternate pin enable: 0=TMR0/PWM0/CAP0 on PA[9], 1=TMR0_/PWM0_/CAP0_ on PB[23] +#define RB_PIN_TMR1 0x02 // RW, TMR1 alternate pin enable: 0=TMR1/PWM1/CAP1 on PA[10], 1=TMR1_/PWM1_/CAP1_ on PB[10] +#define RB_PIN_TMR2 0x04 // RW, TMR2 alternate pin enable: 0=TMR2/PWM2/CAP2 on PA[11], 1=TMR2_/PWM2_/CAP2_ on PB[11] +#define RB_PIN_TMR3 0x08 // RW, TMR3 alternate pin enable: 0=TMR3/PWM3/CAP3 on PA[2], 1=TMR3_/PWM3_/CAP3_ on PB[22] +#define RB_PIN_UART0 0x10 // RW, RXD0/TXD0 alternate pin enable: 0=RXD0/TXD0 on PB[4]/PB[7], 1=RXD0_/TXD0_ on PA[15]/PA[14] +#define RB_PIN_UART1 0x20 // RW, RXD1/TXD1 alternate pin enable: 0=RXD1/TXD1 on PA[8]/PA[9], 1=RXD1_/TXD1_ on PB[12]/PB[13] +#define RB_PIN_UART2 0x40 // RW, RXD2/TXD2 alternate pin enable: 0=RXD2/TXD2 on PA[6]/PA[7], 1=RXD2_/TXD2_ on PB[22]/PB[23] +#define RB_PIN_UART3 0x80 // RW, RXD3/TXD3 alternate pin enable: 0=RXD3/TXD3 on PA[4]/PA[5], 1=RXD3_/TXD3_ on PB[20]/PB[21] +#define RB_PIN_SPI0 0x100 // RW, SCS/SCK0/MOSI/MISO alternate pin enable: 0=SCS/SCK0/MOSI/MISO on PA[12]/PA[13]/PA[14]/PA[15], 1=SCS_/SCK0_/MOSI_/MISO_ on PB[12]/PB[13]/PB[14]/PB[15] +#define RB_PIN_PWMX 0x400 // RW, PWM4/PWM5/PWM7/PWM8/PWM9 alternate pin enable: 0=PWM4/5/7/8/9 on PA[12]/PA[13]/PB[4]/PB[6]/PB[7], 1=PWM4/5/7/8/9 on PA[6]/PA[7]/PB[1]/PB[2]/P[3] +#define RB_PIN_I2C 0x800 // RW, SCL/SDA alternate pin enable: 0=SCL/SDA on PB[13]/PB[12], 1=SCL_/SDA_ on PB[21]/PB[20] +#define RB_PIN_MODEM 0x1000 // RW, DSR/DTR alternate pin enable: 0=DSR/DTR on PB[1]/PB[5], 1=DSR_/DTR_ on PB[14]/PB[15] +#define RB_PIN_INTX 0x2000 // RW, interrupt INT24/INT25 alternate pin enable: 0=INT24/INT25 on PB[8]/PB[9], 1=INT24_/INT25_ on PB[22]/PB[23] +#define RB_PIN_U0_INV 0x4000 // RW, RXD0/RXD0_/TXD0/TXD0_ invert input/output enable: 0=normal input/output, 1=RXD invert input, TXD invert output +#define RB_RF_ANT_SW_EN 0x8000 // RW, RF antenna switch control output enable: 0=disable output, 1=output on PB[16]/PB[17]/PB[18]/PB[19]/PB[20]/PB[21] +#define R16_PIN_ANALOG_IE (*((PUINT16V)0x4000101A)) // RW, analog pin enable and digital input disable +#define RB_PIN_ADC8_9_IE 0x01 // RW, ADC/TouchKey channel 9/8 digital input disable: 0=digital input enable, 1=digital input disable +#define RB_PIN_ADC6_7_IE 0x02 // RW, ADC/TouchKey channel 7/6 digital input disable: 0=digital input enable, 1=digital input disable +#define RB_PIN_ADC10_IE 0x04 // RW, ADC/TouchKey channel 10 digital input disable: 0=digital input enable, 1=digital input disable +#define RB_PIN_ADC11_IE 0x08 // RW, ADC/TouchKey channel 11 digital input disable: 0=digital input enable, 1=digital input disable +#define RB_PIN_USB2_DP_PU 0x10 // RW, USB2 UDP internal pullup resistance enable: 0=enable/disable by RB_UC_DEV_PU_EN, 1=enable pullup, replace RB_UC_DEV_PU_EN under sleep mode +#define RB_PIN_USB2_IE 0x20 // RW, USB2 analog I/O enable: 0=analog I/O disable, 1=analog I/O enable +#define RB_PIN_USB_DP_PU 0x40 // RW, USB UDP internal pullup resistance enable: 0=enable/disable by RB_UC_DEV_PU_EN, 1=enable pullup, replace RB_UC_DEV_PU_EN under sleep mode +#define RB_PIN_USB_IE 0x80 // RW, USB analog I/O enable: 0=analog I/O disable, 1=analog I/O enable +#define RB_PIN_ADC0_IE 0x0200 // RW, ADC/TouchKey channel 0 digital input disable: 0=digital input enable, 1=digital input disable +#define RB_PIN_ADC1_IE 0x0400 // RW, ADC/TouchKey channel 1 digital input disable: 0=digital input enable, 1=digital input disable +#define RB_PIN_ADC12_IE 0x0800 // RW, ADC/TouchKey channel 12 digital input disable: 0=digital input enable, 1=digital input disable +#define RB_PIN_ADC13_IE 0x1000 // RW, ADC/TouchKey channel 13 digital input disable: 0=digital input enable, 1=digital input disable +#define RB_PIN_XT32K_IE 0x2000 // RW, external 32KHz oscillator digital input disable: 0=digital input enable, 1=digital input disable +#define RB_PIN_ADC2_3_IE 0x4000 // RW, ADC/TouchKey channel 2/3 digital input disable: 0=digital input enable, 1=digital input disable +#define RB_PIN_ADC4_5_IE 0x8000 // RW, ADC/TouchKey channel 4/5 digital input disable: 0=digital input enable, 1=digital input disable + +/* System: power management register */ +#define R32_POWER_MANAG (*((PUINT32V)0x40001020)) // RWA, power management register, SAM +#define R16_POWER_PLAN (*((PUINT16V)0x40001020)) // RWA, power plan before sleep instruction, SAM +#define RB_PWR_XROM 0x01 // RWA, power for flash ROM +#define RB_PWR_RAM2K 0x02 // RWA, power for retention 2KB SRAM +#define RB_PWR_CORE 0x04 // RWA, power retention for core and base peripherals +#define RB_PWR_EXTEND 0x08 // RWA, power retention for USB and BLE +#define RB_PWR_RAM30K 0x10 // RWA, power for main SRAM +#define RB_PWR_SYS_EN 0x80 // RWA, power for system +//#define RB_PWR_LDO_EN 0x0100 // RWA, LDO enable +#define RB_PWR_DCDC_EN 0x0200 // RWA, DC/DC converter enable: 0=DC/DC disable and bypass, 1=DC/DC enable +#define RB_PWR_DCDC_PRE 0x0400 // RWA, DC/DC converter pre-enable +#define RB_PWR_PLAN_EN 0x8000 // RWA/WZ, power plan enable, auto clear after sleep executed +#define RB_PWR_MUST_0010 0x1000 // RWA, must write 0010 +#define R16_AUX_POWER_ADJ (*((PUINT8V)0x40001022)) // RWA, aux power adjust control, SAM +#define RB_ULPLDO_ADJ 0x0007 // RWA, Ultra-Low-Power LDO voltage adjust +#define RB_DCDC_CHARGE 0x0080 // RWA, DC/DC aux charge enable + +/* System: battery detector register */ +#define R32_BATTERY_CTRL (*((PUINT32V)0x40001024)) // RWA, battery voltage detector, SAM +#define R8_BAT_DET_CTRL (*((PUINT8V)0x40001024)) // RWA, battery voltage detector control, SAM +#define RB_BAT_DET_EN 0x01 // RWA, battery voltage detector enable if RB_BAT_MON_EN=0 +#define RB_BAT_LOW_VTHX 0x01 // RWA, select monitor threshold voltage if RB_BAT_MON_EN=1 +#define RB_BAT_MON_EN 0x02 // RWA, battery voltage monitor enable under sleep mode +#define RB_BAT_LOWER_IE 0x04 // RWA, interrupt enable for battery lower voltage +#define RB_BAT_LOW_IE 0x08 // RWA, interrupt enable for battery low voltage +// request NMI interrupt if both RB_BAT_LOWER_IE and RB_BAT_LOW_IE enabled +#define R8_BAT_DET_CFG (*((PUINT8V)0x40001025)) // RWA, battery voltage detector configuration, SAM +#define RB_BAT_LOW_VTH 0x03 // RWA, select detector/monitor threshold voltage of battery voltage low +#define R8_BAT_STATUS (*((PUINT8V)0x40001026)) // RO, battery status +#define RB_BAT_STAT_LOWER 0x01 // RO, battery lower voltage status for detector, high action +#define RB_BAT_STAT_LOW 0x02 // RO, battery low voltage status for detector/monitor, high action + +/* System: 32KHz oscillator control register */ +#define R32_OSC32K_CTRL (*((PUINT32V)0x4000102C)) // RWA, 32KHz oscillator control, SAM +#define R16_INT32K_TUNE (*((PUINT16V)0x4000102C)) // RWA, internal 32KHz oscillator tune control, SAM +#define RB_INT32K_TUNE 0x1FFF // RWA, internal 32KHz oscillator frequency tune +#define R8_XT32K_TUNE (*((PUINT8V)0x4000102E)) // RWA, external 32KHz oscillator tune control, SAM +#define RB_XT32K_I_TUNE 0x03 // RWA, external 32KHz oscillator current tune: 00=75% current, 01=standard current, 10=150% current, 11=200% current for startup +#define RB_XT32K_C_LOAD 0xF0 // RWA, external 32KHz oscillator load capacitor tune: Cap = RB_XT32K_C_LOAD + 12pF +#define R8_CK32K_CONFIG (*((PUINT8V)0x4000102F)) // RWA, 32KHz oscillator configure +#define RB_CLK_XT32K_PON 0x01 // RWA, external 32KHz oscillator power on +#define RB_CLK_INT32K_PON 0x02 // RWA, internal 32KHz oscillator power on +#define RB_CLK_OSC32K_XT 0x04 // RWA, 32KHz oscillator source selection: 0=RC, 1=XT +#define RB_CLK_OSC32K_FILT 0x08 // RWA, internal 32KHz oscillator low noise mode disable: 0=enable, 1=disable +#define RB_32K_CLK_PIN 0x80 // RO, 32KHz oscillator clock pin status + +/* System: real-time clock register */ +#define R32_RTC_CTRL (*((PUINT32V)0x40001030)) // RWA, RTC control, SAM +#define R8_RTC_FLAG_CTRL (*((PUINT8V)0x40001030)) // RW, RTC flag and clear control +#define RB_RTC_TMR_CLR 0x10 // RW, set 1 to clear RTC timer action flag, auto clear +#define RB_RTC_TRIG_CLR 0x20 // RW, set 1 to clear RTC trigger action flag, auto clear +#define RB_RTC_TMR_FLAG 0x40 // RO, RTC timer action flag +#define RB_RTC_TRIG_FLAG 0x80 // RO, RTC trigger action flag +#define R8_RTC_MODE_CTRL (*((PUINT8V)0x40001031)) // RWA, RTC mode control, SAM +#define RB_RTC_TMR_MODE 0x07 // RWA, RTC timer mode: 000=0.125S, 001=0.25S, 010=0.5S, 011=1S, 100=2S, 101=4S, 110=8S, 111=16S +#define RB_RTC_IGNORE_B0 0x08 // RWA, force ignore bit0 for trigger mode: 0=compare bit0, 1=ignore bit0 +#define RB_RTC_TMR_EN 0x10 // RWA, RTC timer mode enable +#define RB_RTC_TRIG_EN 0x20 // RWA, RTC trigger mode enable +#define RB_RTC_LOAD_LO 0x40 // RWA, set 1 to load RTC count low word R32_RTC_CNT_32K, auto clear after loaded +#define RB_RTC_LOAD_HI 0x80 // RWA, set 1 to load RTC count high word R32_RTC_CNT_DAY, auto clear after loaded +#define R32_RTC_TRIG (*((PUINT32V)0x40001034)) // RWA, RTC trigger value, SAM +#define R32_RTC_CNT_32K (*((PUINT32V)0x40001038)) // RO, RTC count based 32KHz +#define R16_RTC_CNT_32K (*((PUINT16V)0x40001038)) // RO, RTC count based 32KHz +#define R16_RTC_CNT_2S (*((PUINT16V)0x4000103A)) // RO, RTC count based 2 second +#define R32_RTC_CNT_DAY (*((PUINT32V)0x4000103C)) // RO, RTC count based one day, only low 14 bit + +/*System: Miscellaneous Control register */ +#define R32_MISC_CTRL (*((PUINT32V)0x40001048)) // RWA, miscellaneous control register +#define R8_PLL_CONFIG (*((PUINT8V)0x4000104B)) // RWA, PLL configuration control, SAM +#define RB_FLASH_IO_MOD 0x80 // RWA, flash ROM interface mode, SAM +#define RB_PLL_CFG_DAT 0x7F // RWA, PLL configuration control, SAM + +/* System: 32MHz oscillator control register */ +#define R32_OSC32M_CTRL (*((PUINT32V)0x4000104C)) // RWA, 32MHz oscillator control, SAM +#define R8_XT32M_TUNE (*((PUINT8V)0x4000104E)) // RWA, external 32MHz oscillator tune control, SAM +#define RB_XT32M_I_BIAS 0x03 // RWA, external 32MHz oscillator bias current tune: 00=75% current, 01=standard current, 10=125% current, 11=150% current +#define RB_XT32M_C_LOAD 0x70 // RWA, external 32MHz oscillator load capacitor tune: Cap = RB_XT32M_C_LOAD * 2 + 10pF + +/* System: oscillator frequency calibration register */ +#define R32_OSC_CALIB (*((PUINT32V)0x40001050)) // RWA, oscillator frequency calibration, SAM +#define R16_OSC_CAL_CNT (*((PUINT16V)0x40001050)) // RO, system clock count value for 32KHz multi-cycles +#define RB_OSC_CAL_CNT 0x3FFF // RO, system clock count value for 32KHz multi-cycles +#define RB_OSC_CAL_OV_CLR 0x4000 // RW1, indicate R8_OSC_CAL_OV_CNT not zero, set 1 to clear R8_OSC_CAL_OV_CNT +#define RB_OSC_CAL_IF 0x8000 // RW1, interrupt flag for oscillator capture end, set 1 to clear +#define R8_OSC_CAL_OV_CNT (*((PUINT8V)0x40001052)) // RO, oscillator frequency calibration overflow times +#define R8_OSC_CAL_CTRL (*((PUINT8V)0x40001053)) // RWA, oscillator frequency calibration control, SAM +#define RB_OSC_CNT_TOTAL 0x07 // RWA, total cycles mode for oscillator capture +// RB_OSC_CNT_TOTAL: select total cycles for oscillator capture +// 000: 1 +// 001: 2 +// 010: 4 +// 011: 32 +// 100: 64 +// 101: 128 +// 110: 1024 +// 111: 2047 +#define RB_OSC_CNT_HALT 0x08 // RO, calibration counter halt status: 0=counting, 1=halt for reading count value +#define RB_OSC_CAL_IE 0x10 // RWA, interrupt enable for oscillator capture end +#define RB_OSC_CNT_EN 0x20 // RWA, calibration counter enable +#define RB_OSC_CNT_END 0x40 // RWA, select oscillator capture end mode: 0=normal, 1=append 2 cycles + +/* System: ADC and Touch-key register */ +#define R32_ADC_CTRL (*((PUINT32V)0x40001058)) // RW, ADC control +#define R8_ADC_CHANNEL (*((PUINT8V)0x40001058)) // RW, ADC input channel selection +#define RB_ADC_CH_INX 0x0F // RW, ADC input channel index +#define R8_ADC_CFG (*((PUINT8V)0x40001059)) // RW, ADC configure +#define RB_ADC_POWER_ON 0x01 // RW, ADC power control: 0=power down, 1=power on +#define RB_ADC_BUF_EN 0x02 // RW, ADC input buffer enable +#define RB_ADC_DIFF_EN 0x04 // RW, ADC input channel mode: 0=single-end, 1=differnetial +#define RB_ADC_OFS_TEST 0x08 // RW, enable ADC offset test mode: 0=normal mode, 1=short to test offset +#define RB_ADC_PGA_GAIN 0x30 // RW, set ADC input PGA gain: 00=-12dB, 01=-6dB, 10=0dB, 11=6dB +#define RB_ADC_CLK_DIV 0xC0 // RW, select ADC clock frequency: 00=3.2MHz, 01=8MHz, 10=5.33MHz, 11=4MHz +#define R8_ADC_CONVERT (*((PUINT8V)0x4000105A)) // RW, ADC convert control +#define RB_ADC_START 0x01 // RW, ADC convert start control: 0=stop ADC convert, 1=start an ADC convert, auto clear +#define RB_ADC_EOC_X 0x80 // RO, end of ADC conversion flag +#define R8_TEM_SENSOR (*((PUINT8V)0x4000105B)) // RW, temperature sensor control +#define RB_TEM_SEN_PWR_ON 0x80 // RW, temperature sensor power control: 0=power down, 1=power on +#define R32_ADC_DATA (*((PUINT32V)0x4000105C)) // RO, ADC data and status +#define R16_ADC_DATA (*((PUINT16V)0x4000105C)) // RO, ADC data +#define RB_ADC_DATA 0x0FFF // RO, ADC conversion data +#define R8_ADC_INT_FLAG (*((PUINT8V)0x4000105E)) // RO, ADC interrupt flag register +#define RB_ADC_IF_EOC 0x80 // RO, ADC conversion interrupt flag: 0=free or converting, 1=end of conversion, interrupt action, auto ADC or write R8_ADC_CONVERT or write R8_TKEY_CONVERT to clear flag +#define R32_TKEY_CTRL (*((PUINT8V)0x40001054)) // RW, Touchkey control +#define R8_TKEY_COUNT (*((PUINT8V)0x40001054)) // RW, Touchkey charge and discharge count +#define RB_TKEY_CHARG_CNT 0x1F // RW, Touchkey charge count +#define RB_TKEY_DISCH_CNT 0xE0 // RW, Touchkey discharge count +#define R8_TKEY_CONVERT (*((PUINT8V)0x40001056)) // RW, Touchkey convert control +#define RB_TKEY_START 0x01 // RW, Touchkey convert start control: 0=stop Touchkey convert, 1=start a Touchkey convert, auto clear +#define R8_TKEY_CFG (*((PUINT8V)0x40001057)) // RW, Touchkey configure +#define RB_TKEY_PWR_ON 0x01 // RW, Touchkey power on: 0=power down, 1=power on +#define RB_TKEY_CURRENT 0x02 // RW, Touchkey charge current selection: 0=35uA, 1=70uA +#define RB_TKEY_DRV_EN 0x04 // RW, Touchkey drive shield enable +#define RB_TKEY_PGA_ADJ 0x08 // RW, ADC input PGA speed selection: 0=slow, 1=fast +#define R32_ADC_DMA_CTRL (*((PUINT32V)0x40001060)) // RW, ADC DMA control +#define R8_ADC_CTRL_DMA (*((PUINT8V)0x40001061)) // RW, ADC DMA control +#define RB_ADC_DMA_ENABLE 0x01 // RW, ADC DMA enable +#define RB_ADC_DMA_LOOP 0x04 // RW, ADC DMA address loop enable +#define RB_ADC_IE_DMA_END 0x08 // RW, enable interrupt for ADC DMA completion +#define RB_ADC_IE_EOC 0x10 // RW, enable interrupt for end of ADC conversion +#define RB_ADC_CONT_EN 0x40 // RW, enable contineous conversion ADC +#define RB_ADC_AUTO_EN 0x80 // RW, enable auto continuing ADC for DMA +#define R8_ADC_DMA_IF (*((PUINT8V)0x40001062)) // RW1, ADC interrupt flag +#define RB_ADC_IF_DMA_END 0x08 // RW1, interrupt flag for ADC DMA completion +#define RB_ADC_IF_END_ADC 0x10 // RW1, interrupt flag for end of ADC conversion, DMA for auto ADC or write R8_ADC_CONVERT to clear flag +#define R8_ADC_AUTO_CYCLE (*((PUINT8V)0x40001063)) // RW, auto ADC cycle value, unit is 16 Fsys +#define R32_ADC_DMA_NOW (*((PUINT32V)0x40001064)) // RW, ADC DMA current address +#define R16_ADC_DMA_NOW (*((PUINT16V)0x40001064)) // RW, ADC DMA current address +#define R32_ADC_DMA_BEG (*((PUINT32V)0x40001068)) // RW, ADC DMA begin address +#define R16_ADC_DMA_BEG (*((PUINT16V)0x40001068)) // RW, ADC DMA begin address +#define R32_ADC_DMA_END (*((PUINT32V)0x4000106C)) // RW, ADC DMA end address +#define R16_ADC_DMA_END (*((PUINT16V)0x4000106C)) // RW, ADC DMA end address + +/* System: Flash ROM control register */ +#define R32_FLASH_DATA (*((PUINT32V)0x40001800)) // RO/WO, flash ROM data +#define R32_FLASH_CONTROL (*((PUINT32V)0x40001804)) // RW, flash ROM control +#define R8_FLASH_DATA (*((PUINT8V)0x40001804)) // RO/WO, flash ROM data buffer +#define R8_FLASH_CTRL (*((PUINT8V)0x40001806)) // RW, flash ROM access control +#define R8_FLASH_CFG (*((PUINT8V)0x40001807)) // RW, flash ROM access config, SAM + +/* System: GPIO interrupt control register */ +#define R32_GPIO_INT_EN (*((PUINT32V)0x40001090)) // RW, GPIO interrupt enable +#define R16_PA_INT_EN (*((PUINT16V)0x40001090)) // RW, GPIO PA interrupt enable +#define R16_PB_INT_EN (*((PUINT16V)0x40001092)) // RW, GPIO PB interrupt enable +#define R32_GPIO_INT_MODE (*((PUINT32V)0x40001094)) // RW, GPIO interrupt mode: 0=level action, 1=edge action +#define R16_PA_INT_MODE (*((PUINT16V)0x40001094)) // RW, GPIO PA interrupt mode: 0=level action, 1=edge action +#define R16_PB_INT_MODE (*((PUINT16V)0x40001096)) // RW, GPIO PB interrupt mode: 0=level action, 1=edge action +#define R32_GPIO_INT_IF (*((PUINT32V)0x4000109C)) // RW1, GPIO interrupt flag +#define R16_PA_INT_IF (*((PUINT16V)0x4000109C)) // RW1, GPIO PA interrupt flag +#define R16_PB_INT_IF (*((PUINT16V)0x4000109E)) // RW1, GPIO PB interrupt flag + +/* GPIO PA register */ +#define R32_PA_DIR (*((PUINT32V)0x400010A0)) // RW, GPIO PA I/O direction: 0=in, 1=out +#define R8_PA_DIR_0 (*((PUINT8V)0x400010A0)) // RW, GPIO PA I/O direction byte 0 +#define R8_PA_DIR_1 (*((PUINT8V)0x400010A1)) // RW, GPIO PA I/O direction byte 1 +#define R32_PA_PIN (*((PUINT32V)0x400010A4)) // RO, GPIO PA input +#define R8_PA_PIN_0 (*((PUINT8V)0x400010A4)) // RO, GPIO PA input byte 0 +#define R8_PA_PIN_1 (*((PUINT8V)0x400010A5)) // RO, GPIO PA input byte 1 +#define R32_PA_OUT (*((PUINT32V)0x400010A8)) // RW, GPIO PA output +#define R8_PA_OUT_0 (*((PUINT8V)0x400010A8)) // RW, GPIO PA output byte 0 +#define R8_PA_OUT_1 (*((PUINT8V)0x400010A9)) // RW, GPIO PA output byte 1 +#define R32_PA_CLR (*((PUINT32V)0x400010AC)) // WZ, GPIO PA clear output: 0=keep, 1=clear +#define R8_PA_CLR_0 (*((PUINT8V)0x400010AC)) // WZ, GPIO PA clear output byte 0 +#define R8_PA_CLR_1 (*((PUINT8V)0x400010AD)) // WZ, GPIO PA clear output byte 1 +#define R32_PA_PU (*((PUINT32V)0x400010B0)) // RW, GPIO PA pullup resistance enable +#define R8_PA_PU_0 (*((PUINT8V)0x400010B0)) // RW, GPIO PA pullup resistance enable byte 0 +#define R8_PA_PU_1 (*((PUINT8V)0x400010B1)) // RW, GPIO PA pullup resistance enable byte 1 +#define R32_PA_PD_DRV (*((PUINT32V)0x400010B4)) // RW, PA pulldown for input or PA driving capability for output +#define R8_PA_PD_DRV_0 (*((PUINT8V)0x400010B4)) // RW, PA pulldown for input or PA driving capability for output byte 0 +#define R8_PA_PD_DRV_1 (*((PUINT8V)0x400010B5)) // RW, PA pulldown for input or PA driving capability for output byte 1 + +/* GPIO PB register */ +#define R32_PB_DIR (*((PUINT32V)0x400010C0)) // RW, GPIO PB I/O direction: 0=in, 1=out +#define R8_PB_DIR_0 (*((PUINT8V)0x400010C0)) // RW, GPIO PB I/O direction byte 0 +#define R8_PB_DIR_1 (*((PUINT8V)0x400010C1)) // RW, GPIO PB I/O direction byte 1 +#define R8_PB_DIR_2 (*((PUINT8V)0x400010C2)) // RW, GPIO PB I/O direction byte 2 +#define R32_PB_PIN (*((PUINT32V)0x400010C4)) // RO, GPIO PB input +#define R8_PB_PIN_0 (*((PUINT8V)0x400010C4)) // RO, GPIO PB input byte 0 +#define R8_PB_PIN_1 (*((PUINT8V)0x400010C5)) // RO, GPIO PB input byte 1 +#define R8_PB_PIN_2 (*((PUINT8V)0x400010C6)) // RO, GPIO PB input byte 2 +#define R32_PB_OUT (*((PUINT32V)0x400010C8)) // RW, GPIO PB output +#define R8_PB_OUT_0 (*((PUINT8V)0x400010C8)) // RW, GPIO PB output byte 0 +#define R8_PB_OUT_1 (*((PUINT8V)0x400010C9)) // RW, GPIO PB output byte 1 +#define R8_PB_OUT_2 (*((PUINT8V)0x400010CA)) // RW, GPIO PB output byte 2 +#define R32_PB_CLR (*((PUINT32V)0x400010CC)) // WZ, GPIO PB clear output: 0=keep, 1=clear +#define R8_PB_CLR_0 (*((PUINT8V)0x400010CC)) // WZ, GPIO PB clear output byte 0 +#define R8_PB_CLR_1 (*((PUINT8V)0x400010CD)) // WZ, GPIO PB clear output byte 1 +#define R8_PB_CLR_2 (*((PUINT8V)0x400010CE)) // WZ, GPIO PB clear output byte 2 +#define R32_PB_PU (*((PUINT32V)0x400010D0)) // RW, GPIO PB pullup resistance enable +#define R8_PB_PU_0 (*((PUINT8V)0x400010D0)) // RW, GPIO PB pullup resistance enable byte 0 +#define R8_PB_PU_1 (*((PUINT8V)0x400010D1)) // RW, GPIO PB pullup resistance enable byte 1 +#define R8_PB_PU_2 (*((PUINT8V)0x400010D2)) // RW, GPIO PB pullup resistance enable byte 2 +#define R32_PB_PD_DRV (*((PUINT32V)0x400010D4)) // RW, PB pulldown for input or PB driving capability for output +#define R8_PB_PD_DRV_0 (*((PUINT8V)0x400010D4)) // RW, PB pulldown for input or PB driving capability for output byte 0 +#define R8_PB_PD_DRV_1 (*((PUINT8V)0x400010D5)) // RW, PB pulldown for input or PB driving capability for output byte 1 +#define R8_PB_PD_DRV_2 (*((PUINT8V)0x400010D6)) // RW, PB pulldown for input or PB driving capability for output byte 2 + +/* GPIO register address offset and bit define */ +#define BA_PA ((PUINT8V)0x400010A0) // point GPIO PA base address +#define BA_PB ((PUINT8V)0x400010C0) // point GPIO PB base address +#define GPIO_DIR 0x00 +#define GPIO_DIR_0 0x00 +#define GPIO_DIR_1 0x01 +#define GPIO_DIR_2 0x02 +#define GPIO_PIN 0x04 +#define GPIO_PIN_0 0x04 +#define GPIO_PIN_1 0x05 +#define GPIO_PIN_2 0x06 +#define GPIO_OUT 0x08 +#define GPIO_OUT_0 0x08 +#define GPIO_OUT_1 0x09 +#define GPIO_OUT_2 0x0A +#define GPIO_CLR 0x0C +#define GPIO_CLR_0 0x0C +#define GPIO_CLR_1 0x0D +#define GPIO_CLR_2 0x0E +#define GPIO_PU 0x10 +#define GPIO_PU_0 0x10 +#define GPIO_PU_1 0x11 +#define GPIO_PU_2 0x12 +#define GPIO_PD_DRV 0x14 +#define GPIO_PD_DRV_0 0x14 +#define GPIO_PD_DRV_1 0x15 +#define GPIO_PD_DRV_2 0x16 + +/* GPIO alias name */ +#define bAIN9 (1<<0) // PA0 +#define bSCK1 (1<<0) // PA0 +#define bAIN8 (1<<1) // PA1 +#define bSDO (1<<1) // PA1 +#define bMOSI1 bSDO +#define bAIN7 (1<<2) // PA2 +#define bTMR3_ (1<<2) // PA2 +#define bCAP3_ bTMR3_ +#define bPWM3_ bTMR3_ +#define bSDI (1<<2) // PA2 +#define bMISO1 bSDI +#define bAIN6 (1<<3) // PA3 +#define bAIN0 (1<<4) // PA4 +#define bRXD3 (1<<4) // PA4 +#define bAIN1 (1<<5) // PA5 +#define bTXD3 (1<<5) // PA5 +#define bAIN10 (1<<6) // PA6 +#define bRXD2 (1<<6) // PA6 +#define bPWM4_ (1<<6) // PA6 +#define bAIN11 (1<<7) // PA7 +#define bTXD2 (1<<7) // PA7 +#define bPWM5_ (1<<7) // PA7 +#define bAIN12 (1<<8) // PA8 +#define bRXD1 (1<<8) // PA8 +#define bAIN13 (1<<9) // PA9 +#define bTMR0 (1<<9) // PA9 +#define bCAP0 bTMR0 +#define bPWM0 bTMR0 +#define bTXD1 (1<<9) // PA9 +#define bX32KI (1<<10) // PA10 +#define bTMR1 (1<<10) // PA10 +#define bCAP1 bTMR1 +#define bPWM1 bTMR1 +#define bX32KO (1<<11) // PA11 +#define bTMR2 (1<<11) // PA11 +#define bCAP2 bTMR2 +#define bPWM2 bTMR2 +#define bAIN2 (1<<12) // PA12 +#define bPWM4 (1<<12) // PA12 +#define bSCS (1<<12) // PA12 +#define bAIN3 (1<<13) // PA13 +#define bSCK0 (1<<13) // PA13 +#define bPWM5 (1<<13) // PA13 +#define bAIN4 (1<<14) // PA14 +#define bMOSI (1<<14) // PA14 +#define bTXD0_ (1<<14) // PA14 +#define bAIN5 (1<<15) // PA15 +#define bMISO (1<<15) // PA15 +#define bRXD0_ (1<<15) // PA15 +#define bPWM6 (1<<0) // PB0 +#define bCTS (1<<0) // PB0 +#define bDSR (1<<1) // PB1 +#define bPWM7_ (1<<1) // PB1 +#define bRI (1<<2) // PB2 +#define bPWM8_ (1<<2) // PB2 +#define bDCD (1<<3) // PB3 +#define bPWM9_ (1<<3) // PB3 +#define bPWM7 (1<<4) // PB4 +#define bRXD0 (1<<4) // PB4 +#define bDTR (1<<5) // PB5 +#define bRTS (1<<6) // PB6 +#define bPWM8 (1<<6) // PB6 +#define bTXD0 (1<<7) // PB7 +#define bPWM9 (1<<7) // PB7 +#define bUDM (1<<10) // PB10 +#define bTMR1_ (1<<10) // PB10 +#define bCAP1_ bTMR1_ +#define bPWM1_ bTMR1_ +#define bUDP (1<<11) // PB11 +#define bTMR2_ (1<<11) // PB11 +#define bCAP2_ bTMR2_ +#define bPWM2_ bTMR2_ +#define bU2DM (1<<12) // PB12 +#define bSCS_ (1<<12) // PB12 +#define bSDA (1<<12) // PB12 +#define bRXD1_ (1<<12) // PB12 +#define bU2DP (1<<13) // PB13 +#define bSCK0_ (1<<13) // PB13 +#define bSCL (1<<13) // PB13 +#define bTXD1_ (1<<13) // PB13 +#define bTIO (1<<14) // PB14 +#define bDSR_ (1<<14) // PB14 +#define bMOSI_ (1<<14) // PB14 +#define bPWM10 (1<<14) // PB14 +#define bTCK (1<<15) // PB15 +#define bMISO_ (1<<15) // PB15 +#define bDTR_ (1<<15) // PB15 +#define bSDA_ (1<<20) // PB20 +#define bRXD3_ (1<<20) // PB20 +#define bSCL_ (1<<21) // PB21 +#define bTXD3_ (1<<21) // PB21 +#define bRXD2_ (1<<22) // PB22 +#define bTMR3 (1<<22) // PB22 +#define bCAP3 bTMR3 +#define bPWM3 bTMR3 +#define bRST (1<<23) // PB23 +#define bTMR0_ (1<<23) // PB23 +#define bCAP0_ bTMR0_ +#define bPWM0_ bTMR0_ +#define bTXD2_ (1<<23) // PB23 +#define bPWM11 (1<<23) // PB23 + +/* Timer0 register */ +#define R32_TMR0_CONTROL (*((PUINT32V)0x40002000)) // RW, TMR0 control +#define R8_TMR0_CTRL_MOD (*((PUINT8V)0x40002000)) // RW, TMR0 mode control +#define R8_TMR0_INTER_EN (*((PUINT8V)0x40002002)) // RW, TMR0 interrupt enable +#define R32_TMR0_STATUS (*((PUINT32V)0x40002004)) // RW, TMR0 status +#define R8_TMR0_INT_FLAG (*((PUINT8V)0x40002006)) // RW1, TMR0 interrupt flag +#define R8_TMR0_FIFO_COUNT (*((PUINT8V)0x40002007)) // RO, TMR0 FIFO count status +#define R32_TMR0_COUNT (*((PUINT32V)0x40002008)) // RO, TMR0 current count +#define R16_TMR0_COUNT (*((PUINT16V)0x40002008)) // RO, TMR0 current count +#define R8_TMR0_COUNT (*((PUINT8V)0x40002008)) // RO, TMR0 current count +#define R32_TMR0_CNT_END (*((PUINT32V)0x4000200C)) // RW, TMR0 end count value, only low 26 bit +#define R32_TMR0_FIFO (*((PUINT32V)0x40002010)) // RO/WO, TMR0 FIFO register, only low 26 bit +#define R16_TMR0_FIFO (*((PUINT16V)0x40002010)) // RO/WO, TMR0 FIFO register +#define R8_TMR0_FIFO (*((PUINT8V)0x40002010)) // RO/WO, TMR0 FIFO register + +/* Timer1 register */ +#define R32_TMR1_CONTROL (*((PUINT32V)0x40002400)) // RW, TMR1 control +#define R8_TMR1_CTRL_MOD (*((PUINT8V)0x40002400)) // RW, TMR1 mode control +#define R8_TMR1_CTRL_DMA (*((PUINT8V)0x40002401)) // RW, TMR1 DMA control +#define R8_TMR1_INTER_EN (*((PUINT8V)0x40002402)) // RW, TMR1 interrupt enable +#define R32_TMR1_STATUS (*((PUINT32V)0x40002404)) // RW, TMR1 status +#define R8_TMR1_INT_FLAG (*((PUINT8V)0x40002406)) // RW1, TMR1 interrupt flag +#define R8_TMR1_FIFO_COUNT (*((PUINT8V)0x40002407)) // RO, TMR1 FIFO count status +#define R32_TMR1_COUNT (*((PUINT32V)0x40002408)) // RO, TMR1 current count +#define R16_TMR1_COUNT (*((PUINT16V)0x40002408)) // RO, TMR1 current count +#define R8_TMR1_COUNT (*((PUINT8V)0x40002408)) // RO, TMR1 current count +#define R32_TMR1_CNT_END (*((PUINT32V)0x4000240C)) // RW, TMR1 end count value, only low 26 bit +#define R32_TMR1_FIFO (*((PUINT32V)0x40002410)) // RO/WO, TMR1 FIFO register, only low 26 bit +#define R16_TMR1_FIFO (*((PUINT16V)0x40002410)) // RO/WO, TMR1 FIFO register +#define R8_TMR1_FIFO (*((PUINT8V)0x40002410)) // RO/WO, TMR1 FIFO register +#define R32_TMR1_DMA_NOW (*((PUINT32V)0x40002414)) // RW, TMR1 DMA current address +#define R16_TMR1_DMA_NOW (*((PUINT16V)0x40002414)) // RW, TMR1 DMA current address +#define R32_TMR1_DMA_BEG (*((PUINT32V)0x40002418)) // RW, TMR1 DMA begin address +#define R16_TMR1_DMA_BEG (*((PUINT16V)0x40002418)) // RW, TMR1 DMA begin address +#define R32_TMR1_DMA_END (*((PUINT32V)0x4000241C)) // RW, TMR1 DMA end address +#define R16_TMR1_DMA_END (*((PUINT16V)0x4000241C)) // RW, TMR1 DMA end address + +/* Timer2 register */ +#define R32_TMR2_CONTROL (*((PUINT32V)0x40002800)) // RW, TMR2 control +#define R8_TMR2_CTRL_MOD (*((PUINT8V)0x40002800)) // RW, TMR2 mode control +#define R8_TMR2_CTRL_DMA (*((PUINT8V)0x40002801)) // RW, TMR2 DMA control +#define R8_TMR2_INTER_EN (*((PUINT8V)0x40002802)) // RW, TMR2 interrupt enable +#define R32_TMR2_STATUS (*((PUINT32V)0x40002804)) // RW, TMR2 status +#define R8_TMR2_INT_FLAG (*((PUINT8V)0x40002806)) // RW1, TMR2 interrupt flag +#define R8_TMR2_FIFO_COUNT (*((PUINT8V)0x40002807)) // RO, TMR2 FIFO count status +#define R32_TMR2_COUNT (*((PUINT32V)0x40002808)) // RO, TMR2 current count +#define R16_TMR2_COUNT (*((PUINT16V)0x40002808)) // RO, TMR2 current count +#define R8_TMR2_COUNT (*((PUINT8V)0x40002808)) // RO, TMR2 current count +#define R32_TMR2_CNT_END (*((PUINT32V)0x4000280C)) // RW, TMR2 end count value, only low 26 bit +#define R32_TMR2_FIFO (*((PUINT32V)0x40002810)) // RO/WO, TMR2 FIFO register, only low 26 bit +#define R16_TMR2_FIFO (*((PUINT16V)0x40002810)) // RO/WO, TMR2 FIFO register +#define R8_TMR2_FIFO (*((PUINT8V)0x40002810)) // RO/WO, TMR2 FIFO register +#define R32_TMR2_DMA_NOW (*((PUINT32V)0x40002814)) // RW, TMR2 DMA current address +#define R16_TMR2_DMA_NOW (*((PUINT16V)0x40002814)) // RW, TMR2 DMA current address +#define R32_TMR2_DMA_BEG (*((PUINT32V)0x40002818)) // RW, TMR2 DMA begin address +#define R16_TMR2_DMA_BEG (*((PUINT16V)0x40002818)) // RW, TMR2 DMA begin address +#define R32_TMR2_DMA_END (*((PUINT32V)0x4000281C)) // RW, TMR2 DMA end address +#define R16_TMR2_DMA_END (*((PUINT16V)0x4000281C)) // RW, TMR2 DMA end address + +/* Timer3 register */ +#define R32_TMR3_CONTROL (*((PUINT32V)0x40002C00)) // RW, TMR3 control +#define R8_TMR3_CTRL_MOD (*((PUINT8V)0x40002C00)) // RW, TMR3 mode control +#define R8_TMR3_INTER_EN (*((PUINT8V)0x40002C02)) // RW, TMR3 interrupt enable +#define R32_TMR3_STATUS (*((PUINT32V)0x40002C04)) // RW, TMR3 status +#define R8_TMR3_INT_FLAG (*((PUINT8V)0x40002C06)) // RW1, TMR3 interrupt flag +#define R8_TMR3_FIFO_COUNT (*((PUINT8V)0x40002C07)) // RO, TMR3 FIFO count status +#define R32_TMR3_COUNT (*((PUINT32V)0x40002C08)) // RO, TMR3 current count +#define R16_TMR3_COUNT (*((PUINT16V)0x40002C08)) // RO, TMR3 current count +#define R8_TMR3_COUNT (*((PUINT8V)0x40002C08)) // RO, TMR3 current count +#define R32_TMR3_CNT_END (*((PUINT32V)0x40002C0C)) // RW, TMR3 end count value, only low 26 bit +#define R32_TMR3_FIFO (*((PUINT32V)0x40002C10)) // RO/WO, TMR3 FIFO register, only low 26 bit +#define R16_TMR3_FIFO (*((PUINT16V)0x40002C10)) // RO/WO, TMR3 FIFO register +#define R8_TMR3_FIFO (*((PUINT8V)0x40002C10)) // RO/WO, TMR3 FIFO register + +/* Timer register address offset and bit define */ +#define TMR_FIFO_SIZE 8 // timer FIFO size (depth) +#define BA_TMR0 ((PUINT8V)0x40002000) // point TMR0 base address +#define BA_TMR1 ((PUINT8V)0x40002400) // point TMR1 base address +#define BA_TMR2 ((PUINT8V)0x40002800) // point TMR2 base address +#define BA_TMR3 ((PUINT8V)0x40002C00) // point TMR3 base address +#define TMR_CTRL_MOD 0 +#define RB_TMR_MODE_IN 0x01 // RW, timer in mode: 0=timer/PWM, 1=capture/count +#define RB_TMR_ALL_CLEAR 0x02 // RW, force clear timer FIFO and count +#define RB_TMR_COUNT_EN 0x04 // RW, timer count enable +#define RB_TMR_OUT_EN 0x08 // RW, timer output enable +#define RB_TMR_OUT_POLAR 0x10 // RW, timer PWM output polarity: 0=default low and high action, 1=default high and low action +#define RB_TMR_CAP_COUNT 0x10 // RW, count sub-mode if RB_TMR_MODE_IN=1: 0=capture, 1=count +#define RB_TMR_PWM_REPEAT 0xC0 // RW, timer PWM repeat mode: 00=1, 01=4, 10=8, 11-16 +#define RB_TMR_CAP_EDGE 0xC0 // RW, timer capture edge mode: 00=disable, 01=edge change, 10=fall to fall, 11-rise to rise +#define TMR_CTRL_DMA 1 +#define RB_TMR_DMA_ENABLE 0x01 // RW, timer1/2 DMA enable +#define RB_TMR_DMA_LOOP 0x04 // RW, timer1/2 DMA address loop enable +#define TMR_INTER_EN 2 +#define RB_TMR_IE_CYC_END 0x01 // RW, enable interrupt for timer capture count timeout or PWM cycle end +#define RB_TMR_IE_DATA_ACT 0x02 // RW, enable interrupt for timer capture input action or PWM trigger +#define RB_TMR_IE_FIFO_HF 0x04 // RW, enable interrupt for timer FIFO half (capture fifo >=4 or PWM fifo <=3) +#define RB_TMR_IE_DMA_END 0x08 // RW, enable interrupt for timer1/2 DMA completion +#define RB_TMR_IE_FIFO_OV 0x10 // RW, enable interrupt for timer FIFO overflow +#define TMR_INT_FLAG 6 +#define RB_TMR_IF_CYC_END 0x01 // RW1, interrupt flag for timer capture count timeout or PWM cycle end +#define RB_TMR_IF_DATA_ACT 0x02 // RW1, interrupt flag for timer capture input action or PWM trigger +#define RB_TMR_IF_FIFO_HF 0x04 // RW1, interrupt flag for timer FIFO half (capture fifo >=4 or PWM fifo <=3) +#define RB_TMR_IF_DMA_END 0x08 // RW1, interrupt flag for timer1/2 DMA completion +#define RB_TMR_IF_FIFO_OV 0x10 // RW1, interrupt flag for timer FIFO overflow +#define TMR_FIFO_COUNT 7 +#define TMR_COUNT 0x08 +#define TMR_CNT_END 0x0C +#define TMR_FIFO 0x10 +#define TMR_DMA_NOW 0x14 +#define TMR_DMA_BEG 0x18 +#define TMR_DMA_END 0x1C + +/* UART0 register */ +#define R32_UART0_CTRL (*((PUINT32V)0x40003000)) // RW, UART0 control +#define R8_UART0_MCR (*((PUINT8V)0x40003000)) // RW, UART0 modem control +#define R8_UART0_IER (*((PUINT8V)0x40003001)) // RW, UART0 interrupt enable +#define R8_UART0_FCR (*((PUINT8V)0x40003002)) // RW, UART0 FIFO control +#define R8_UART0_LCR (*((PUINT8V)0x40003003)) // RW, UART0 line control +#define R32_UART0_STAT (*((PUINT32V)0x40003004)) // RO, UART0 status +#define R8_UART0_IIR (*((PUINT8V)0x40003004)) // RO, UART0 interrupt identification +#define R8_UART0_LSR (*((PUINT8V)0x40003005)) // RO, UART0 line status +#define R8_UART0_MSR (*((PUINT8V)0x40003006)) // RO, UART0 modem status +#define R32_UART0_FIFO (*((PUINT32V)0x40003008)) // RW, UART0 data or FIFO port +#define R8_UART0_RBR (*((PUINT8V)0x40003008)) // RO, UART0 receiver buffer, receiving byte +#define R8_UART0_THR (*((PUINT8V)0x40003008)) // WO, UART0 transmitter holding, transmittal byte +#define R8_UART0_RFC (*((PUINT8V)0x4000300A)) // RO, UART0 receiver FIFO count +#define R8_UART0_TFC (*((PUINT8V)0x4000300B)) // RO, UART0 transmitter FIFO count +#define R32_UART0_SETUP (*((PUINT32V)0x4000300C)) // RW, UART0 setup +#define R16_UART0_DL (*((PUINT16V)0x4000300C)) // RW, UART0 divisor latch +#define R8_UART0_DLL (*((PUINT8V)0x4000300C)) // RW, UART0 divisor latch LSB byte +#define R8_UART0_DLM (*((PUINT8V)0x4000300D)) // RW, UART0 divisor latch MSB byte +#define R8_UART0_DIV (*((PUINT8V)0x4000300E)) // RW, UART0 pre-divisor latch byte, only low 7 bit, from 1 to 0/128 +#define R8_UART0_ADR (*((PUINT8V)0x4000300F)) // RW, UART0 slave address: 0xFF=disable, other=enable + +/* UART1 register */ +#define R32_UART1_CTRL (*((PUINT32V)0x40003400)) // RW, UART1 control +#define R8_UART1_MCR (*((PUINT8V)0x40003400)) // RW, UART1 modem control +#define R8_UART1_IER (*((PUINT8V)0x40003401)) // RW, UART1 interrupt enable +#define R8_UART1_FCR (*((PUINT8V)0x40003402)) // RW, UART1 FIFO control +#define R8_UART1_LCR (*((PUINT8V)0x40003403)) // RW, UART1 line control +#define R32_UART1_STAT (*((PUINT32V)0x40003404)) // RO, UART1 status +#define R8_UART1_IIR (*((PUINT8V)0x40003404)) // RO, UART1 interrupt identification +#define R8_UART1_LSR (*((PUINT8V)0x40003405)) // RO, UART1 line status +#define R32_UART1_FIFO (*((PUINT32V)0x40003408)) // RW, UART1 data or FIFO port +#define R8_UART1_RBR (*((PUINT8V)0x40003408)) // RO, UART1 receiver buffer, receiving byte +#define R8_UART1_THR (*((PUINT8V)0x40003408)) // WO, UART1 transmitter holding, transmittal byte +#define R8_UART1_RFC (*((PUINT8V)0x4000340A)) // RO, UART1 receiver FIFO count +#define R8_UART1_TFC (*((PUINT8V)0x4000340B)) // RO, UART1 transmitter FIFO count +#define R32_UART1_SETUP (*((PUINT32V)0x4000340C)) // RW, UART1 setup +#define R16_UART1_DL (*((PUINT16V)0x4000340C)) // RW, UART1 divisor latch +#define R8_UART1_DLL (*((PUINT8V)0x4000340C)) // RW, UART1 divisor latch LSB byte +#define R8_UART1_DLM (*((PUINT8V)0x4000340D)) // RW, UART1 divisor latch MSB byte +#define R8_UART1_DIV (*((PUINT8V)0x4000340E)) // RW, UART1 pre-divisor latch byte, only low 7 bit, from 1 to 0/128 + +/* UART2 register */ +#define R32_UART2_CTRL (*((PUINT32V)0x40003800)) // RW, UART2 control +#define R8_UART2_MCR (*((PUINT8V)0x40003800)) // RW, UART2 modem control +#define R8_UART2_IER (*((PUINT8V)0x40003801)) // RW, UART2 interrupt enable +#define R8_UART2_FCR (*((PUINT8V)0x40003802)) // RW, UART2 FIFO control +#define R8_UART2_LCR (*((PUINT8V)0x40003803)) // RW, UART2 line control +#define R32_UART2_STAT (*((PUINT32V)0x40003804)) // RO, UART2 status +#define R8_UART2_IIR (*((PUINT8V)0x40003804)) // RO, UART2 interrupt identification +#define R8_UART2_LSR (*((PUINT8V)0x40003805)) // RO, UART2 line status +#define R32_UART2_FIFO (*((PUINT32V)0x40003808)) // RW, UART2 data or FIFO port +#define R8_UART2_RBR (*((PUINT8V)0x40003808)) // RO, UART2 receiver buffer, receiving byte +#define R8_UART2_THR (*((PUINT8V)0x40003808)) // WO, UART2 transmitter holding, transmittal byte +#define R8_UART2_RFC (*((PUINT8V)0x4000380A)) // RO, UART2 receiver FIFO count +#define R8_UART2_TFC (*((PUINT8V)0x4000380B)) // RO, UART2 transmitter FIFO count +#define R32_UART2_SETUP (*((PUINT32V)0x4000380C)) // RW, UART2 setup +#define R16_UART2_DL (*((PUINT16V)0x4000380C)) // RW, UART2 divisor latch +#define R8_UART2_DLL (*((PUINT8V)0x4000380C)) // RW, UART2 divisor latch LSB byte +#define R8_UART2_DLM (*((PUINT8V)0x4000380D)) // RW, UART2 divisor latch MSB byte +#define R8_UART2_DIV (*((PUINT8V)0x4000380E)) // RW, UART2 pre-divisor latch byte, only low 7 bit, from 1 to 0/128 + +/* UART3 register */ +#define R32_UART3_CTRL (*((PUINT32V)0x40003C00)) // RW, UART3 control +#define R8_UART3_MCR (*((PUINT8V)0x40003C00)) // RW, UART3 modem control +#define R8_UART3_IER (*((PUINT8V)0x40003C01)) // RW, UART3 interrupt enable +#define R8_UART3_FCR (*((PUINT8V)0x40003C02)) // RW, UART3 FIFO control +#define R8_UART3_LCR (*((PUINT8V)0x40003C03)) // RW, UART3 line control +#define R32_UART3_STAT (*((PUINT32V)0x40003C04)) // RO, UART3 status +#define R8_UART3_IIR (*((PUINT8V)0x40003C04)) // RO, UART3 interrupt identification +#define R8_UART3_LSR (*((PUINT8V)0x40003C05)) // RO, UART3 line status +#define R32_UART3_FIFO (*((PUINT32V)0x40003C08)) // RW, UART3 data or FIFO port +#define R8_UART3_RBR (*((PUINT8V)0x40003C08)) // RO, UART3 receiver buffer, receiving byte +#define R8_UART3_THR (*((PUINT8V)0x40003C08)) // WO, UART3 transmitter holding, transmittal byte +#define R8_UART3_RFC (*((PUINT8V)0x40003C0A)) // RO, UART3 receiver FIFO count +#define R8_UART3_TFC (*((PUINT8V)0x40003C0B)) // RO, UART3 transmitter FIFO count +#define R32_UART3_SETUP (*((PUINT32V)0x40003C0C)) // RW, UART3 setup +#define R16_UART3_DL (*((PUINT16V)0x40003C0C)) // RW, UART3 divisor latch +#define R8_UART3_DLL (*((PUINT8V)0x40003C0C)) // RW, UART3 divisor latch LSB byte +#define R8_UART3_DLM (*((PUINT8V)0x40003C0D)) // RW, UART3 divisor latch MSB byte +#define R8_UART3_DIV (*((PUINT8V)0x40003C0E)) // RW, UART3 pre-divisor latch byte, only low 7 bit, from 1 to 0/128 + +/* UART register address offset and bit define */ +#define UART_FIFO_SIZE 8 // UART FIFO size (depth) +#define UART_RECV_RDY_SZ 7 // the max FIFO trigger level for UART receiver data available +#define BA_UART0 ((PUINT8V)0x40003000) // point UART0 base address +#define BA_UART1 ((PUINT8V)0x40003400) // point UART1 base address +#define BA_UART2 ((PUINT8V)0x40003800) // point UART2 base address +#define BA_UART3 ((PUINT8V)0x40003C00) // point UART3 base address +#define UART_MCR 0 +#define RB_MCR_DTR 0x01 // RW, UART0 control DTR +#define RB_MCR_RTS 0x02 // RW, UART0 control RTS +#define RB_MCR_OUT1 0x04 // RW, UART0 control OUT1 +#define RB_MCR_OUT2 0x08 // RW, UART control OUT2 +#define RB_MCR_INT_OE 0x08 // RW, UART interrupt output enable +#define RB_MCR_LOOP 0x10 // RW, UART0 enable local loop back +#define RB_MCR_AU_FLOW_EN 0x20 // RW, UART0 enable autoflow control +#define RB_MCR_TNOW 0x40 // RW, UART0 enable TNOW output on DTR pin +#define RB_MCR_HALF 0x80 // RW, UART0 enable half-duplex +#define UART_IER 1 +#define RB_IER_RECV_RDY 0x01 // RW, UART interrupt enable for receiver data ready +#define RB_IER_THR_EMPTY 0x02 // RW, UART interrupt enable for THR empty +#define RB_IER_LINE_STAT 0x04 // RW, UART interrupt enable for receiver line status +#define RB_IER_MODEM_CHG 0x08 // RW, UART0 interrupt enable for modem status change +#define RB_IER_DTR_EN 0x10 // RW, UART0 DTR/TNOW output pin enable +#define RB_IER_RTS_EN 0x20 // RW, UART0 RTS output pin enable +#define RB_IER_TXD_EN 0x40 // RW, UART TXD pin enable +#define RB_IER_RESET 0x80 // WZ, UART software reset control, high action, auto clear +#define UART_FCR 2 +#define RB_FCR_FIFO_EN 0x01 // RW, UART FIFO enable +#define RB_FCR_RX_FIFO_CLR 0x02 // WZ, clear UART receiver FIFO, high action, auto clear +#define RB_FCR_TX_FIFO_CLR 0x04 // WZ, clear UART transmitter FIFO, high action, auto clear +#define RB_FCR_FIFO_TRIG 0xC0 // RW, UART receiver FIFO trigger level: 00-1byte, 01-2bytes, 10-4bytes, 11-7bytes +#define UART_LCR 3 +#define RB_LCR_WORD_SZ 0x03 // RW, UART word bit length: 00-5bit, 01-6bit, 10-7bit, 11-8bit +#define RB_LCR_STOP_BIT 0x04 // RW, UART stop bit length: 0-1bit, 1-2bit +#define RB_LCR_PAR_EN 0x08 // RW, UART parity enable +#define RB_LCR_PAR_MOD 0x30 // RW, UART parity mode: 00-odd, 01-even, 10-mark, 11-space +#define RB_LCR_BREAK_EN 0x40 // RW, UART break control enable +#define RB_LCR_DLAB 0x80 // RW, UART reserved bit +#define RB_LCR_GP_BIT 0x80 // RW, UART general purpose bit +#define UART_IIR 4 +#define RB_IIR_NO_INT 0x01 // RO, UART no interrupt flag: 0=interrupt action, 1=no interrupt +#define RB_IIR_INT_MASK 0x0F // RO, UART interrupt flag bit mask +#define RB_IIR_FIFO_ID 0xC0 // RO, UART FIFO enabled flag +#define UART_LSR 5 +#define RB_LSR_DATA_RDY 0x01 // RO, UART receiver fifo data ready status +#define RB_LSR_OVER_ERR 0x02 // RZ, UART receiver overrun error +#define RB_LSR_PAR_ERR 0x04 // RZ, UART receiver parity error +#define RB_LSR_FRAME_ERR 0x08 // RZ, UART receiver frame error +#define RB_LSR_BREAK_ERR 0x10 // RZ, UART receiver break error +#define RB_LSR_TX_FIFO_EMP 0x20 // RO, UART transmitter fifo empty status +#define RB_LSR_TX_ALL_EMP 0x40 // RO, UART transmitter all empty status +#define RB_LSR_ERR_RX_FIFO 0x80 // RO, indicate error in UART receiver fifo +#define UART_MSR 6 +#define RB_MSR_CTS_CHG 0x01 // RZ, UART0 CTS changed status, high action +#define RB_MSR_DSR_CHG 0x02 // RZ, UART0 DSR changed status, high action +//#define RB_MSR_RI_CHG 0x04 // RZ, UART0 RI changed status, high action +//#define RB_MSR_DCD_CHG 0x08 // RZ, UART0 DCD changed status, high action +#define RB_MSR_CTS 0x10 // RO, UART0 CTS action status +#define RB_MSR_DSR 0x20 // RO, UART0 DSR action status +//#define RB_MSR_RI 0x40 // RO, UART0 RI action status +//#define RB_MSR_DCD 0x80 // RO, UART0 DCD action status +#define UART_RBR 8 +#define UART_THR 8 +#define UART_RFC 0x0A +#define UART_TFC 0x0B +#define UART_DLL 0x0C +#define UART_DLM 0x0D +#define UART_DIV 0x0E +#define UART_ADR 0x0F + +/* UART interrupt identification values for IIR bits 3:0 */ +#define UART_II_SLV_ADDR 0x0E // RO, UART0 slave address match +#define UART_II_LINE_STAT 0x06 // RO, UART interrupt by receiver line status +#define UART_II_RECV_RDY 0x04 // RO, UART interrupt by receiver data available +#define UART_II_RECV_TOUT 0x0C // RO, UART interrupt by receiver fifo timeout +#define UART_II_THR_EMPTY 0x02 // RO, UART interrupt by THR empty +#define UART_II_MODEM_CHG 0x00 // RO, UART0 interrupt by modem status change +#define UART_II_NO_INTER 0x01 // RO, no UART interrupt is pending + +/* SPI0 register */ +#define R32_SPI0_CONTROL (*((PUINT32V)0x40004000)) // RW, SPI0 control +#define R8_SPI0_CTRL_MOD (*((PUINT8V)0x40004000)) // RW, SPI0 mode control +#define R8_SPI0_CTRL_CFG (*((PUINT8V)0x40004001)) // RW, SPI0 configuration control +#define R8_SPI0_INTER_EN (*((PUINT8V)0x40004002)) // RW, SPI0 interrupt enable +#define R8_SPI0_CLOCK_DIV (*((PUINT8V)0x40004003)) // RW, SPI0 master clock divisor +#define R8_SPI0_SLAVE_PRE (*((PUINT8V)0x40004003)) // RW, SPI0 slave preset value +#define R32_SPI0_STATUS (*((PUINT32V)0x40004004)) // RW, SPI0 status +#define R8_SPI0_BUFFER (*((PUINT8V)0x40004004)) // RO, SPI0 data buffer +#define R8_SPI0_RUN_FLAG (*((PUINT8V)0x40004005)) // RO, SPI0 work flag +#define R8_SPI0_INT_FLAG (*((PUINT8V)0x40004006)) // RW1, SPI0 interrupt flag +#define R8_SPI0_FIFO_COUNT (*((PUINT8V)0x40004007)) // RO, SPI0 FIFO count status +#define R32_SPI0_TOTAL_CNT (*((PUINT32V)0x4000400C)) // RW, SPI0 total byte count, only low 12 bit +#define R16_SPI0_TOTAL_CNT (*((PUINT16V)0x4000400C)) // RW, SPI0 total byte count, only low 12 bit +#define R32_SPI0_FIFO (*((PUINT32V)0x40004010)) // RW, SPI0 FIFO register +#define R8_SPI0_FIFO (*((PUINT8V)0x40004010)) // RO/WO, SPI0 FIFO register +#define R8_SPI0_FIFO_COUNT1 (*((PUINT8V)0x40004013)) // RO, SPI0 FIFO count status +#define R32_SPI0_DMA_NOW (*((PUINT32V)0x40004014)) // RW, SPI0 DMA current address +#define R16_SPI0_DMA_NOW (*((PUINT16V)0x40004014)) // RW, SPI0 DMA current address +#define R32_SPI0_DMA_BEG (*((PUINT32V)0x40004018)) // RW, SPI0 DMA begin address +#define R16_SPI0_DMA_BEG (*((PUINT16V)0x40004018)) // RW, SPI0 DMA begin address +#define R32_SPI0_DMA_END (*((PUINT32V)0x4000401C)) // RW, SPI0 DMA end address +#define R16_SPI0_DMA_END (*((PUINT16V)0x4000401C)) // RW, SPI0 DMA end address + +/* SPI1 register */ +#define R32_SPI1_CONTROL (*((PUINT32V)0x40004400)) // RW, SPI1 control +#define R8_SPI1_CTRL_MOD (*((PUINT8V)0x40004400)) // RW, SPI1 mode control +#define R8_SPI1_CTRL_CFG (*((PUINT8V)0x40004401)) // RW, SPI1 configuration control +#define R8_SPI1_INTER_EN (*((PUINT8V)0x40004402)) // RW, SPI1 interrupt enable +#define R8_SPI1_CLOCK_DIV (*((PUINT8V)0x40004403)) // RW, SPI1 master clock divisor +#define R32_SPI1_STATUS (*((PUINT32V)0x40004404)) // RW, SPI1 status +#define R8_SPI1_BUFFER (*((PUINT8V)0x40004404)) // RO, SPI1 data buffer +#define R8_SPI1_RUN_FLAG (*((PUINT8V)0x40004405)) // RO, SPI1 work flag +#define R8_SPI1_INT_FLAG (*((PUINT8V)0x40004406)) // RW1, SPI1 interrupt flag +#define R8_SPI1_FIFO_COUNT (*((PUINT8V)0x40004407)) // RO, SPI1 FIFO count status +#define R32_SPI1_TOTAL_CNT (*((PUINT32V)0x4000440C)) // RW, SPI1 total byte count, only low 12 bit +#define R16_SPI1_TOTAL_CNT (*((PUINT16V)0x4000440C)) // RW, SPI1 total byte count, only low 12 bit +#define R32_SPI1_FIFO (*((PUINT32V)0x40004410)) // RW, SPI1 FIFO register +#define R8_SPI1_FIFO (*((PUINT8V)0x40004410)) // RO/WO, SPI1 FIFO register +#define R8_SPI1_FIFO_COUNT1 (*((PUINT8V)0x40004413)) // RO, SPI1 FIFO count status + +/* SPI register address offset and bit define */ +#define SPI_FIFO_SIZE 8 // SPI FIFO size (depth) +#define BA_SPI0 ((PUINT8V)0x40004000) // point SPI0 base address +#define BA_SPI1 ((PUINT8V)0x40004400) // point SPI1 base address +#define SPI_CTRL_MOD 0 +#define RB_SPI_MODE_SLAVE 0x01 // RW, SPI0 slave mode: 0=master/host, 1=slave/device +#define RB_SPI_ALL_CLEAR 0x02 // RW, force clear SPI FIFO and count +#define RB_SPI_2WIRE_MOD 0x04 // RW, SPI0 enable 2 wire mode for slave: 0=3wire(SCK0,MOSI,MISO), 1=2wire(SCK0,MISO=MXSX) +#define RB_SPI_MST_SCK_MOD 0x08 // RW, SPI master clock mode: 0=mode 0, 1=mode 3 +#define RB_SPI_SLV_CMD_MOD 0x08 // RW, SPI0 slave command mode: 0=byte stream, 1=first byte command +#define RB_SPI_FIFO_DIR 0x10 // RW, SPI FIFO direction: 0=out(write @master mode), 1=in(read @master mode) +#define RB_SPI_SCK_OE 0x20 // RW, SPI SCK output enable +#define RB_SPI_MOSI_OE 0x40 // RW, SPI MOSI output enable +#define RB_SPI1_SDO_OE 0x40 // RW, SPI1 SDO output enable +#define RB_SPI_MISO_OE 0x80 // RW, SPI MISO output enable +#define RB_SPI1_SDI_OE 0x80 // RW, SPI1 SDI output enable, SPI1 enable 2 wire mode: 0=3wire(SCK1,SDO,SDI), 1=2wire(SCK1,SDI=SDX) +#define SPI_CTRL_CFG 1 +#define RB_SPI_DMA_ENABLE 0x01 // RW, SPI0 DMA enable +#define RB_SPI_DMA_LOOP 0x04 // RW, SPI0 DMA address loop enable +#define RB_SPI_AUTO_IF 0x10 // RW, enable buffer/FIFO accessing to auto clear RB_SPI_IF_BYTE_END interrupt flag +#define RB_SPI_BIT_ORDER 0x20 // RW, SPI bit data order: 0=MSB first, 1=LSB first +#define RB_SPI_MST_DLY_EN 0x40 // RW, SPI master input delay enable +#define SPI_INTER_EN 2 +#define RB_SPI_IE_CNT_END 0x01 // RW, enable interrupt for SPI total byte count end +#define RB_SPI_IE_BYTE_END 0x02 // RW, enable interrupt for SPI byte exchanged +#define RB_SPI_IE_FIFO_HF 0x04 // RW, enable interrupt for SPI FIFO half +#define RB_SPI_IE_DMA_END 0x08 // RW, enable interrupt for SPI0 DMA completion +#define RB_SPI_IE_FIFO_OV 0x10 // RW, enable interrupt for SPI0 FIFO overflow +#define RB_SPI_IE_FST_BYTE 0x80 // RW, enable interrupt for SPI0 slave mode first byte received +#define SPI_CLOCK_DIV 3 +#define SPI_SLAVE_PRESET 3 +#define SPI_BUFFER 4 +#define SPI_RUN_FLAG 5 +#define RB_SPI_SLV_CMD_ACT 0x10 // RO, SPI0 slave first byte / command flag +#define RB_SPI_FIFO_READY 0x20 // RO, SPI FIFO ready status +#define RB_SPI_SLV_CS_LOAD 0x40 // RO, SPI0 slave chip-select loading status +#define RB_SPI_SLV_SELECT 0x80 // RO, SPI0 slave selection status +#define SPI_INT_FLAG 6 +#define RB_SPI_IF_CNT_END 0x01 // RW1, interrupt flag for SPI total byte count end +#define RB_SPI_IF_BYTE_END 0x02 // RW1, interrupt flag for SPI byte exchanged +#define RB_SPI_IF_FIFO_HF 0x04 // RW1, interrupt flag for SPI FIFO half (RB_SPI_FIFO_DIR ? >=4bytes : <4bytes) +#define RB_SPI_IF_DMA_END 0x08 // RW1, interrupt flag for SPI0 DMA completion +#define RB_SPI_IF_FIFO_OV 0x10 // RW1, interrupt flag for SPI0 FIFO overflow +#define RB_SPI_FREE 0x40 // RO, current SPI free status +#define RB_SPI_IF_FST_BYTE 0x80 // RW1, interrupt flag for SPI0 slave mode first byte received +#define SPI_FIFO_COUNT 7 +#define SPI_TOTAL_CNT 0x0C +#define SPI_FIFO 0x10 +#define SPI_DMA_NOW 0x14 +#define SPI_DMA_BEG 0x18 +#define SPI_DMA_END 0x1C + +/* I2C register */ +#define R16_I2C_CTRL1 (*((PUINT16V)0x40004800)) // RW, I2C control 1 +#define R16_I2C_CTRL2 (*((PUINT16V)0x40004804)) // RW, I2C control 2 +#define R16_I2C_OADDR1 (*((PUINT16V)0x40004808)) // RW, I2C own address register 1 +#define R16_I2C_OADDR2 (*((PUINT16V)0x4000480C)) // RW, I2C own address register 2 +#define R16_I2C_DATAR (*((PUINT16V)0x40004810)) // RW, I2C data register +#define R16_I2C_STAR1 (*((PUINT16V)0x40004814)) // R0, I2C stauts register 1 +#define R16_I2C_STAR2 (*((PUINT16V)0x40004818)) // R0, I2C status register 2 +#define R8_I2C_PEC (*((PUINT8V) 0x40004819)) // R0, I2C Packet error checking register +#define R16_I2C_CKCFGR (*((PUINT16V)0x4000481C)) // RW, I2C clock control register +#define R16_I2C_RTR (*((PUINT16V)0x40004820)) // RW, I2C trise register + +/* I2C register address offset and bit define */ +#define BA_I2C ((PUINT8V)0x40004800) // point I2C base address +#define I2C_CTRL1 0 +#define RB_I2C_PE 0x0001 // RW, Peripheral enable +#define RB_I2C_SMBUS 0x0002 // RW, SMBUS mode: 0=I2C mode, 1=SMBUS mode +#define RB_I2C_SMBTYPE 0x0008 // RW, SMBus type: 0=Device, 1=Host +#define RB_I2C_EBARP 0x0010 // RW, ARP enable +#define RB_I2C_ENPEC 0x0020 // RW, PEC ebable +#define RB_I2C_ENGC 0x0040 // RW, General call enable +#define RB_I2C_NOSTRETCH 0x0080 // RW, Clock stretching disable (Slave mode) +#define RB_I2C_START 0x0100 // RW, Start generation: master mode: 0=no start, 1=repeated start; slave mode: 0=no start, 1=start at bus free +#define RB_I2C_STOP 0x0200 // RW, Stop generation: master mode: 0=no stop, 1=stop after the current byte transfer or after the current Start condition is sent; slave mode: 0=no stop, 1=Release the SCL and SDA lines after the current byte transfer +#define RB_I2C_ACK 0x0400 // RW, Acknowledge enable +#define RB_I2C_POS 0x0800 // RW, Acknowledge/PEC Position (for data reception) +#define RB_I2C_PEC 0x1000 // RW, Packet error checking: 0=No PEC transfer, 1=PEC transfer (in Tx or Rx mode) +#define RB_I2C_ALERT 0x2000 // RW, SMBus alert: 0=Releases SMBA pin high, 1=Drives SMBA pin low. +#define RB_I2C_SWRST 0x8000 // RW, Software reset +#define I2C_CTRL2 4 +#define RB_I2C_FREQ 0x003F // RW, Peripheral clock frequency, The minimum allowed frequency is 2 MHz,the maximum frequency is 36 MHz +#define RB_I2C_ITERREN 0x0100 // RW, Error interrupt enable +#define RB_I2C_ITEVTEN 0x0200 // RW, Event interrupt enable +#define RB_I2C_ITBUFEN 0x0400 // RW, Buffer interrupt enable +#define I2C_OADDR1 8 +#define RB_I2C_ADD0 0x0001 // RW, bit0 of address in 10-bit addressing mode +#define RB_I2C_ADD7_1 0x00FE // RW, bit[7:1] of address +#define RB_I2C_ADD9_8 0x0300 // RW, bit[9:8] of address in 10-bit addressing mode +#define RB_I2C_MUST1 0x4000 // RW, Should always be kept at 1 +#define RB_I2C_ADDMODE 0x8000 // RW, Addressing mode (slave mode): 0=7-bit slave address, 1=10-bit slave address +#define I2C_OADDR2 12 +#define RB_I2C_ENDUAL 0x0001 // RW, Dual addressing mode enable +#define RB_I2C_ADD2 0x00FE // RW, bit[7:1] of address2 +#define I2C_DATAR 16 +#define I2C_STAR1 20 +#define RB_I2C_SB 0x0001 // RW0, Start bit flag (Master mode) +#define RB_I2C_ADDR 0x0002 // RW0, Address sent (master mode)/matched (slave mode) flag +#define RB_I2C_BTF 0x0004 // RO, Byte transfer finished flag +#define RB_I2C_ADD10 0x0008 // RO, 10-bit header sent flag (Master mode) +#define RB_I2C_STOPF 0x0010 // RO, Stop detection flag (slave mode) +#define RB_I2C_RxNE 0x0040 // RO, Data register not empty flag (receivers) +#define RB_I2C_TxE 0x0080 // RO, Data register empty flag (transmitters) +#define RB_I2C_BERR 0x0100 // RW0, Bus error flag +#define RB_I2C_ARLO 0x0200 // RW0, Arbitration lost flag (master mode) +#define RB_I2C_AF 0x0400 // RW0, Acknowledge failure flag +#define RB_I2C_OVR 0x0800 // RW0, Overrun/Underrun flag +#define RB_I2C_PECERR 0x1000 // RW0, PEC Error flag in reception +#define RB_I2C_TIMEOUT 0x4000 // RW0, Timeout or Tlow error flag +#define RB_I2C_SMBALERT 0x8000 // RW0, SMBus alert flag +#define I2C_STAR2 24 +#define RB_I2C_MSL 0x0001 // RO, Mode statu: 0=Slave mode, 1=Master mode +#define RB_I2C_BUSY 0x0002 // RO, Bus busy flag +#define RB_I2C_TRA 0x0004 // RO, Trans flag: 0=data bytes received, 1=data bytes transmitted +#define RB_I2C_GENCALL 0x0010 // RO, General call address (Slave mode) received flag +#define RB_I2C_SMBDEFAULT 0x0020 // RO, SMBus device default address (Slave mode) received flag +#define RB_I2C_SMBHOST 0x0040 // RO, SMBus host header (Slave mode) received flag +#define RB_I2C_DUALF 0x0080 // RO, Dual flag (Slave mode): 0=Received address matched with OAR1, 1=Received address matched with OAR2 +#define RB_I2C_PECX 0xFF00 // RO, Packet error checking register +#define I2C_CKCFGR 28 +#define RB_I2C_CCR 0x0FFF // RW, Controls the SCL clock in Fm/Sm mode (Master mode) +#define RB_I2C_DUTY 0x4000 // RW, Fm mode duty cycle: 0=L/H=2, 1=L/H=16/9 +#define RB_I2C_F_S 0x8000 // RW, I2C master mode selection: 0=standard mode, 1=fast mode +#define I2C_RTR 32 +#define RB_I2C_TRISE 0x003F // RW, Maximum rise time in Fm/Sm mode (Master mode) + +/* PWM4/5/6/7/8/9/10/11 register */ +#define R32_PWM_CONTROL (*((PUINT32V)0x40005000)) // RW, PWM control +#define R8_PWM_OUT_EN (*((PUINT8V)0x40005000)) // RW, PWM output enable control +#define R8_PWM_POLAR (*((PUINT8V)0x40005001)) // RW, PWM output polarity control +#define R8_PWM_CONFIG (*((PUINT8V)0x40005002)) // RW, PWM configuration +#define R8_PWM_CLOCK_DIV (*((PUINT8V)0x40005003)) // RW, PWM clock divisor +#define R32_PWM4_7_DATA (*((PUINT32V)0x40005004)) // RW, PWM4-7 data holding +#define R8_PWM4_DATA (*((PUINT8V)0x40005004)) // RW, PWM4 data holding +#define R8_PWM5_DATA (*((PUINT8V)0x40005005)) // RW, PWM5 data holding +#define R8_PWM6_DATA (*((PUINT8V)0x40005006)) // RW, PWM6 data holding +#define R8_PWM7_DATA (*((PUINT8V)0x40005007)) // RW, PWM7 data holding +#define R32_PWM8_11_DATA (*((PUINT32V)0x40005008)) // RW, PWM8-11 data holding +#define R8_PWM8_DATA (*((PUINT8V)0x40005008)) // RW, PWM8 data holding +#define R8_PWM9_DATA (*((PUINT8V)0x40005009)) // RW, PWM9 data holding +#define R8_PWM10_DATA (*((PUINT8V)0x4000500A)) // RW, PWM10 data holding +#define R8_PWM11_DATA (*((PUINT8V)0x4000500B)) // RW, PWM11 data holding +#define R8_PWM_INT_CTRL (*((PUINT32V)0x4000500C)) // RW, PWM interrupt control +#define RB_PWM_IE_CYC 0x01 // RW, enable interrupt for PWM cycle end +#define RB_PWM_CYC_PRE 0x02 // RW, select PWM cycle interrupt point: 0=after count 0xFE (0x7E for 7 bits mode...), 1=after count 0xF0 (0x70 for 7 bits mode...) +#define RB_PWM_IF_CYC 0x80 // RW1, interrupt flag for PWM cycle end + +/* PWM4/5/6/7/8/9/10/11 register address offset and bit define */ +#define BA_PWMX ((PUINT8V)0x40005000) // point PWM4/5/6/7/8/9/10/11 base address +#define PWM_OUT_EN 0 +#define RB_PWM4_OUT_EN 0x01 // RW, PWM4 output enable +#define RB_PWM5_OUT_EN 0x02 // RW, PWM5 output enable +#define RB_PWM6_OUT_EN 0x04 // RW, PWM6 output enable +#define RB_PWM7_OUT_EN 0x08 // RW, PWM7 output enable +#define RB_PWM8_OUT_EN 0x10 // RW, PWM8 output enable +#define RB_PWM9_OUT_EN 0x20 // RW, PWM9 output enable +#define RB_PWM10_OUT_EN 0x40 // RW, PWM10 output enable +#define RB_PWM11_OUT_EN 0x80 // RW, PWM11 output enable +#define PWM_POLAR 1 +#define RB_PWM4_POLAR 0x01 // RW, PWM4 output polarity: 0=default low and high action, 1=default high and low action +#define RB_PWM5_POLAR 0x02 // RW, PWM5 output polarity: 0=default low and high action, 1=default high and low action +#define RB_PWM6_POLAR 0x04 // RW, PWM6 output polarity: 0=default low and high action, 1=default high and low action +#define RB_PWM7_POLAR 0x08 // RW, PWM7 output polarity: 0=default low and high action, 1=default high and low action +#define RB_PWM8_POLAR 0x10 // RW, PWM8 output polarity: 0=default low and high action, 1=default high and low action +#define RB_PWM9_POLAR 0x20 // RW, PWM9 output polarity: 0=default low and high action, 1=default high and low action +#define RB_PWM10_POLAR 0x40 // RW, PWM10 output polarity: 0=default low and high action, 1=default high and low action +#define RB_PWM11_POLAR 0x80 // RW, PWM11 output polarity: 0=default low and high action, 1=default high and low action +#define PWM_CONFIG 2 +#define RB_PWM_CYCLE_SEL 0x01 // RW, PWM cycle selection: 0=256/128/64/32 clocks, 1=255/127/63/31 clocks +#define RB_PWM_STAG_ST 0x02 // RO, PWM stagger cycle status +#define RB_PWM_CYC_MOD 0x0C // RW, PWM data width mode: 00=8 bits data, 01=7 bits data, 10=6 bits data, 11=5 bits data +#define RB_PWM4_5_STAG_EN 0x10 // RW, PWM4/5 stagger output enable: 0=independent output, 1=stagger output +#define RB_PWM6_7_STAG_EN 0x20 // RW, PWM6/7 stagger output enable: 0=independent output, 1=stagger output +#define RB_PWM8_9_STAG_EN 0x40 // RW, PWM8/9 stagger output enable: 0=independent output, 1=stagger output +#define RB_PWM10_11_STAG_EN 0x80 // RW, PWM10/11 stagger output enable: 0=independent output, 1=stagger output +#define PWM_CLOCK_DIV 3 +#define PWM4_DATA_HOLD 4 +#define PWM5_DATA_HOLD 5 +#define PWM6_DATA_HOLD 6 +#define PWM7_DATA_HOLD 7 +#define PWM8_DATA_HOLD 8 +#define PWM9_DATA_HOLD 9 +#define PWM10_DATA_HOLD 10 +#define PWM11_DATA_HOLD 11 + +/* Address space define */ +#define BA_CODE ((PUINT32)0x00000000) // point code base address +#define SZ_CODE 0x00080000 // code size +#define BA_SFR ((PUINT32)0x40000000) // point SFR base address +#define SZ_SFR 0x00010000 // SFR size +#define BA_RAM ((PUINT32)0x20000000) // point RAM base address +#define SZ_RAM 0x00008000 // RAM size + +/* Special Program Space */ +#define DATA_FLASH_ADDR 0x70000 // start address of Data-Flash +#define DATA_FLASH_SIZE 0x8000 // size of Data-Flash +#define BOOT_LOAD_ADDR 0x78000 // start address of boot loader program +#define BOOT_LOAD_SIZE 0x6000 // size of boot loader program +#define BOOT_LOAD_CFG 0x7E000 // start address of configuration information for boot loader program +#define ROM_CFG_ADDR 0x7F000 // chip configuration information address + +/*----- Reference Information --------------------------------------------*/ +#define ID_CH583 0x83 // chip ID +#define ID_CH582 0x82 // chip ID +#define ID_CH581 0x81 // chip ID + +/* Interrupt routine address and interrupt number */ +#define INT_ID_TMR0 16 // interrupt number for Timer0 +#define INT_ID_GPIO_A 17 // interrupt number for GPIO port A +#define INT_ID_GPIO_B 18 // interrupt number for GPIO port B +#define INT_ID_SPI0 19 // interrupt number for SPI0 +#define INT_ID_BLEB 20 // interrupt number for BLEBB +#define INT_ID_BLEL 21 // interrupt number for BLELLE +#define INT_ID_USB 22 // interrupt number for USB +#define INT_ID_USB2 23 // interrupt number for USB2 +#define INT_ID_TMR1 24 // interrupt number for Timer1 +#define INT_ID_TMR2 25 // interrupt number for Timer2 +#define INT_ID_UART0 26 // interrupt number for UART0 +#define INT_ID_UART1 27 // interrupt number for UART1 +#define INT_ID_RTC 28 // interrupt number for RTC +#define INT_ID_ADC 29 // interrupt number for ADC and TouchKey +#define INT_ID_I2C 30 // interrupt number for I2C +#define INT_ID_PWMX_SPI1 31 // interrupt number for PWM4~11 and SPI1 +#define INT_ID_TMR3 32 // interrupt number for Timer3 +#define INT_ID_UART2 33 // interrupt number for UART2 +#define INT_ID_UART3 34 // interrupt number for UART3 +#define INT_ID_WDOG_BAT 35 // interrupt number for Watch-Dog timer and Battery low voltage +#define INT_VEC_ENTRY_SZ 4 // size of each interrupt vector entry +#define INT_ADDR_TMR0 (INT_ID_TMR0*INT_VEC_ENTRY_SZ) // interrupt vector address for Timer0 +#define INT_ADDR_GPIO_A (INT_ID_GPIO_A*INT_VEC_ENTRY_SZ) // interrupt vector address for GPIO port A +#define INT_ADDR_GPIO_B (INT_ID_GPIO_B*INT_VEC_ENTRY_SZ) // interrupt vector address for GPIO port B +#define INT_ADDR_SPI0 (INT_ID_SPI0*INT_VEC_ENTRY_SZ) // interrupt vector address for SPI0 +#define INT_ADDR_BLEB (INT_ID_BLEB*INT_VEC_ENTRY_SZ) // interrupt vector address for BLEBB +#define INT_ADDR_BLEL (INT_ID_BLEL*INT_VEC_ENTRY_SZ) // interrupt vector address for BLELLE +#define INT_ADDR_USB (INT_ID_USB*INT_VEC_ENTRY_SZ) // interrupt vector address for USB +#define INT_ADDR_USB2 (INT_ID_USB2*INT_VEC_ENTRY_SZ) // interrupt vector address for USB2 +#define INT_ADDR_TMR1 (INT_ID_TMR1*INT_VEC_ENTRY_SZ) // interrupt vector address for Timer1 +#define INT_ADDR_TMR2 (INT_ID_TMR2*INT_VEC_ENTRY_SZ) // interrupt vector address for Timer2 +#define INT_ADDR_UART0 (INT_ID_UART0*INT_VEC_ENTRY_SZ) // interrupt vector address for UART0 +#define INT_ADDR_UART1 (INT_ID_UART1*INT_VEC_ENTRY_SZ) // interrupt vector address for UART1 +#define INT_ADDR_RTC (INT_ID_RTC*INT_VEC_ENTRY_SZ) // interrupt vector address for RTC +#define INT_ADDR_ADC (INT_ID_ADC*INT_VEC_ENTRY_SZ) // interrupt vector address for ADC and TouchKey +#define INT_ADDR_I2C (INT_ID_I2C*INT_VEC_ENTRY_SZ) // interrupt vector address for I2C +#define INT_ADDR_PWMX_SPI1 (INT_ID_PWMX_SPI1*INT_VEC_ENTRY_SZ) // interrupt vector address for PWM4~11 and SPI1 +#define INT_ADDR_TMR3 (INT_ID_TMR3*INT_VEC_ENTRY_SZ) // interrupt vector address for Timer3 +#define INT_ADDR_UART2 (INT_ID_UART2*INT_VEC_ENTRY_SZ) // interrupt vector address for UART2 +#define INT_ADDR_UART3 (INT_ID_UART3*INT_VEC_ENTRY_SZ) // interrupt vector address for UART3 +#define INT_ADDR_WDOG_BAT (INT_ID_WDOG_BAT*INT_VEC_ENTRY_SZ) // interrupt vector address for Watch-Dog timer and Battery low voltage + +#ifndef TABLE_IRQN +#define __PFIC_PRIO_BITS 2 /*!< uses 8 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0 /*!< Set to 1 if different SysTick Config is used */ +typedef enum IRQn +{ + Reset_IRQn = 1, + NMI_IRQn = 2, /*!< Non Maskable Interrupt */ + EXC_IRQn = 3, /*!< Exceptions Interrupt */ + SysTick_IRQn = 12, /*!< System timer Interrupt */ + SWI_IRQn = 14, /*!< software Interrupt */ + TMR0_IRQn = 16, + GPIO_A_IRQn = 17, + GPIO_B_IRQn = 18, + SPI0_IRQn = 19, + BLEB_IRQn = 20, + BLEL_IRQn = 21, + USB_IRQn = 22, + USB2_IRQn = 23, + TMR1_IRQn = 24, + TMR2_IRQn = 25, + UART0_IRQn = 26, + UART1_IRQn = 27, + RTC_IRQn = 28, + ADC_IRQn = 29, + I2C_IRQn = 30, + PWMX_SPI1_IRQn = 31, + TMR3_IRQn = 32, + UART2_IRQn = 33, + UART3_IRQn = 34, + WDOG_BAT_IRQn = 35 +} IRQn_Type; +#endif + + +#ifdef __cplusplus +} +#endif + +#endif // __CH583SFR_H__ + + +#ifndef __CH583USBSFR_H__ +#define __CH583USBSFR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************/ +/* Peripheral memory map */ +/******************************************************************************/ +/* usb addresses +// USB: +8000H - 83FFH */ +#define USB_BASE_ADDR (0x40008000) +#define USB2_BASE_ADDR (0x40008400) +#define BA_USB ((PUINT8V)0x40008000) // point USB base address +#define BA_USB2 ((PUINT8V)0x40008400) // point USB2 base address + +/* USB */ +#define R32_USB_CONTROL (*((PUINT32V)0x40008000)) // USB control & interrupt enable & device address +#define R8_USB_CTRL (*((PUINT8V)0x40008000)) // USB base control +#define RB_UC_HOST_MODE 0x80 // enable USB host mode: 0=device mode, 1=host mode +#define RB_UC_LOW_SPEED 0x40 // enable USB low speed: 0=12Mbps, 1=1.5Mbps +#define RB_UC_DEV_PU_EN 0x20 // USB device enable and internal pullup resistance enable +#define RB_UC_SYS_CTRL1 0x20 // USB system control high bit +#define RB_UC_SYS_CTRL0 0x10 // USB system control low bit +#define MASK_UC_SYS_CTRL 0x30 // bit mask of USB system control +// bUC_HOST_MODE & bUC_SYS_CTRL1 & bUC_SYS_CTRL0: USB system control +// 0 00: disable USB device and disable internal pullup resistance +// 0 01: enable USB device and disable internal pullup resistance, need RB_PIN_USB_DP_PU=1 or need external pullup resistance +// 0 1x: enable USB device and enable internal pullup resistance +// 1 00: enable USB host and normal status +// 1 01: enable USB host and force UDP/UDM output SE0 state +// 1 10: enable USB host and force UDP/UDM output J state +// 1 11: enable USB host and force UDP/UDM output resume or K state +#define RB_UC_INT_BUSY 0x08 // enable automatic responding busy for device mode or automatic pause for host mode during interrupt flag UIF_TRANSFER valid +#define RB_UC_RESET_SIE 0x04 // force reset USB SIE, need software clear +#define RB_UC_CLR_ALL 0x02 // force clear FIFO and count of USB +#define RB_UC_DMA_EN 0x01 // DMA enable and DMA interrupt enable for USB + +#define R8_UDEV_CTRL (*((PUINT8V)0x40008001)) // USB device physical prot control +#define RB_UD_PD_DIS 0x80 // disable USB UDP/UDM pulldown resistance: 0=enable pulldown, 1=disable +#define RB_UD_DP_PIN 0x20 // ReadOnly: indicate current UDP pin level +#define RB_UD_DM_PIN 0x10 // ReadOnly: indicate current UDM pin level +#define RB_UD_LOW_SPEED 0x04 // enable USB physical port low speed: 0=full speed, 1=low speed +#define RB_UD_GP_BIT 0x02 // general purpose bit +#define RB_UD_PORT_EN 0x01 // enable USB physical port I/O: 0=disable, 1=enable + +#define R8_UHOST_CTRL R8_UDEV_CTRL // USB host physical prot control +#define RB_UH_PD_DIS 0x80 // disable USB UDP/UDM pulldown resistance: 0=enable pulldown, 1=disable +#define RB_UH_DP_PIN 0x20 // ReadOnly: indicate current UDP pin level +#define RB_UH_DM_PIN 0x10 // ReadOnly: indicate current UDM pin level +#define RB_UH_LOW_SPEED 0x04 // enable USB port low speed: 0=full speed, 1=low speed +#define RB_UH_BUS_RESET 0x02 // control USB bus reset: 0=normal, 1=force bus reset +#define RB_UH_PORT_EN 0x01 // enable USB port: 0=disable, 1=enable port, automatic disabled if USB device detached + +#define R8_USB_INT_EN (*((PUINT8V)0x40008002)) // USB interrupt enable +#define RB_UIE_DEV_SOF 0x80 // enable interrupt for SOF received for USB device mode +#define RB_UIE_DEV_NAK 0x40 // enable interrupt for NAK responded for USB device mode +#define RB_UIE_FIFO_OV 0x10 // enable interrupt for FIFO overflow +#define RB_UIE_HST_SOF 0x08 // enable interrupt for host SOF timer action for USB host mode +#define RB_UIE_SUSPEND 0x04 // enable interrupt for USB suspend or resume event +#define RB_UIE_TRANSFER 0x02 // enable interrupt for USB transfer completion +#define RB_UIE_DETECT 0x01 // enable interrupt for USB device detected event for USB host mode +#define RB_UIE_BUS_RST 0x01 // enable interrupt for USB bus reset event for USB device mode + +#define R8_USB_DEV_AD (*((PUINT8V)0x40008003)) // USB device address +#define RB_UDA_GP_BIT 0x80 // general purpose bit +#define MASK_USB_ADDR 0x7F // bit mask for USB device address + +#define R32_USB_STATUS (*((PUINT32V)0x40008004)) // USB miscellaneous status & interrupt flag & interrupt status +#define R8_USB_MIS_ST (*((PUINT8V)0x40008005)) // USB miscellaneous status +#define RB_UMS_SOF_PRES 0x80 // RO, indicate host SOF timer presage status +#define RB_UMS_SOF_ACT 0x40 // RO, indicate host SOF timer action status for USB host +#define RB_UMS_SIE_FREE 0x20 // RO, indicate USB SIE free status +#define RB_UMS_R_FIFO_RDY 0x10 // RO, indicate USB receiving FIFO ready status (not empty) +#define RB_UMS_BUS_RESET 0x08 // RO, indicate USB bus reset status +#define RB_UMS_SUSPEND 0x04 // RO, indicate USB suspend status +#define RB_UMS_DM_LEVEL 0x02 // RO, indicate UDM level saved at device attached to USB host +#define RB_UMS_DEV_ATTACH 0x01 // RO, indicate device attached status on USB host + +#define R8_USB_INT_FG (*((PUINT8V)0x40008006)) // USB interrupt flag +#define RB_U_IS_NAK 0x80 // RO, indicate current USB transfer is NAK received +#define RB_U_TOG_OK 0x40 // RO, indicate current USB transfer toggle is OK +#define RB_U_SIE_FREE 0x20 // RO, indicate USB SIE free status +#define RB_UIF_FIFO_OV 0x10 // FIFO overflow interrupt flag for USB, direct bit address clear or write 1 to clear +#define RB_UIF_HST_SOF 0x08 // host SOF timer interrupt flag for USB host, direct bit address clear or write 1 to clear +#define RB_UIF_SUSPEND 0x04 // USB suspend or resume event interrupt flag, direct bit address clear or write 1 to clear +#define RB_UIF_TRANSFER 0x02 // USB transfer completion interrupt flag, direct bit address clear or write 1 to clear +#define RB_UIF_DETECT 0x01 // device detected event interrupt flag for USB host mode, direct bit address clear or write 1 to clear +#define RB_UIF_BUS_RST 0x01 // bus reset event interrupt flag for USB device mode, direct bit address clear or write 1 to clear + +#define R8_USB_INT_ST (*((PUINT8V)0x40008007)) // USB interrupt status +#define RB_UIS_SETUP_ACT 0x80 // RO, indicate SETUP token & 8 bytes setup request received for USB device mode +#define RB_UIS_TOG_OK 0x40 // RO, indicate current USB transfer toggle is OK +#define RB_UIS_TOKEN1 0x20 // RO, current token PID code bit 1 received for USB device mode +#define RB_UIS_TOKEN0 0x10 // RO, current token PID code bit 0 received for USB device mode +#define MASK_UIS_TOKEN 0x30 // RO, bit mask of current token PID code received for USB device mode +#define UIS_TOKEN_OUT 0x00 +#define UIS_TOKEN_SOF 0x10 +#define UIS_TOKEN_IN 0x20 +#define UIS_TOKEN_SETUP 0x30 +// bUIS_TOKEN1 & bUIS_TOKEN0: current token PID code received for USB device mode, keep last status during SETUP token, clear RB_UIF_TRANSFER ( RB_UIF_TRANSFER from 1 to 0 ) to set free +// 00: OUT token PID received +// 01: SOF token PID received +// 10: IN token PID received +// 11: free +#define MASK_UIS_ENDP 0x0F // RO, bit mask of current transfer endpoint number for USB device mode +#define MASK_UIS_H_RES 0x0F // RO, bit mask of current transfer handshake response for USB host mode: 0000=no response, time out from device, others=handshake response PID received + +#define R8_USB_RX_LEN (*((PUINT8V)0x40008008)) // USB receiving length +#define R32_USB_BUF_MODE (*((PUINT32V)0x4000800C)) // USB endpoint buffer mode +#define R8_UEP4_1_MOD (*((PUINT8V)0x4000800C)) // endpoint 4/1 mode +#define RB_UEP1_RX_EN 0x80 // enable USB endpoint 1 receiving (OUT) +#define RB_UEP1_TX_EN 0x40 // enable USB endpoint 1 transmittal (IN) +#define RB_UEP1_BUF_MOD 0x10 // buffer mode of USB endpoint 1 +// bUEPn_RX_EN & bUEPn_TX_EN & bUEPn_BUF_MOD: USB endpoint 1/2/3 buffer mode, buffer start address is UEPn_DMA +// 0 0 x: disable endpoint and disable buffer +// 1 0 0: 64 bytes buffer for receiving (OUT endpoint) +// 1 0 1: dual 64 bytes buffer by toggle bit bUEP_R_TOG selection for receiving (OUT endpoint), total=128bytes +// 0 1 0: 64 bytes buffer for transmittal (IN endpoint) +// 0 1 1: dual 64 bytes buffer by toggle bit bUEP_T_TOG selection for transmittal (IN endpoint), total=128bytes +// 1 1 0: 64 bytes buffer for receiving (OUT endpoint) + 64 bytes buffer for transmittal (IN endpoint), total=128bytes +// 1 1 1: dual 64 bytes buffer by bUEP_R_TOG selection for receiving (OUT endpoint) + dual 64 bytes buffer by bUEP_T_TOG selection for transmittal (IN endpoint), total=256bytes +#define RB_UEP4_RX_EN 0x08 // enable USB endpoint 4 receiving (OUT) +#define RB_UEP4_TX_EN 0x04 // enable USB endpoint 4 transmittal (IN) +// bUEP4_RX_EN & bUEP4_TX_EN: USB endpoint 4 buffer mode, buffer start address is UEP0_DMA +// 0 0: single 64 bytes buffer for endpoint 0 receiving & transmittal (OUT & IN endpoint) +// 1 0: single 64 bytes buffer for endpoint 0 receiving & transmittal (OUT & IN endpoint) + 64 bytes buffer for endpoint 4 receiving (OUT endpoint), total=128bytes +// 0 1: single 64 bytes buffer for endpoint 0 receiving & transmittal (OUT & IN endpoint) + 64 bytes buffer for endpoint 4 transmittal (IN endpoint), total=128bytes +// 1 1: single 64 bytes buffer for endpoint 0 receiving & transmittal (OUT & IN endpoint) +// + 64 bytes buffer for endpoint 4 receiving (OUT endpoint) + 64 bytes buffer for endpoint 4 transmittal (IN endpoint), total=192bytes + +#define R8_UEP2_3_MOD (*((PUINT8V)0x4000800D)) // endpoint 2/3 mode +#define RB_UEP3_RX_EN 0x80 // enable USB endpoint 3 receiving (OUT) +#define RB_UEP3_TX_EN 0x40 // enable USB endpoint 3 transmittal (IN) +#define RB_UEP3_BUF_MOD 0x10 // buffer mode of USB endpoint 3 +#define RB_UEP2_RX_EN 0x08 // enable USB endpoint 2 receiving (OUT) +#define RB_UEP2_TX_EN 0x04 // enable USB endpoint 2 transmittal (IN) +#define RB_UEP2_BUF_MOD 0x01 // buffer mode of USB endpoint 2 + +#define R8_UEP567_MOD (*((PUINT8V)0x4000800E)) // endpoint 5/6/7 mode +#define RB_UEP7_RX_EN 0x20 // enable USB endpoint 7 receiving (OUT) +#define RB_UEP7_TX_EN 0x10 // enable USB endpoint 7 transmittal (IN) +#define RB_UEP6_RX_EN 0x08 // enable USB endpoint 6 receiving (OUT) +#define RB_UEP6_TX_EN 0x04 // enable USB endpoint 6 transmittal (IN) +#define RB_UEP5_RX_EN 0x02 // enable USB endpoint 5 receiving (OUT) +#define RB_UEP5_TX_EN 0x01 // enable USB endpoint 5 transmittal (IN) +// bUEPn_RX_EN & bUEPn_TX_EN: USB endpoint 5/6/7 buffer mode, buffer start address is UEPn_DMA +// 0 0: disable endpoint and disable buffer +// 1 0: 64 bytes buffer for receiving (OUT endpoint) +// 0 1: 64 bytes buffer for transmittal (IN endpoint) +// 1 1: 64 bytes buffer for receiving (OUT endpoint) + 64 bytes buffer for transmittal (IN endpoint), total=128bytes + +#define R8_UH_EP_MOD R8_UEP2_3_MOD //host endpoint mode +#define RB_UH_EP_TX_EN 0x40 // enable USB host OUT endpoint transmittal +#define RB_UH_EP_TBUF_MOD 0x10 // buffer mode of USB host OUT endpoint +// bUH_EP_TX_EN & bUH_EP_TBUF_MOD: USB host OUT endpoint buffer mode, buffer start address is UH_TX_DMA +// 0 x: disable endpoint and disable buffer +// 1 0: 64 bytes buffer for transmittal (OUT endpoint) +// 1 1: dual 64 bytes buffer by toggle bit bUH_T_TOG selection for transmittal (OUT endpoint), total=128bytes +#define RB_UH_EP_RX_EN 0x08 // enable USB host IN endpoint receiving +#define RB_UH_EP_RBUF_MOD 0x01 // buffer mode of USB host IN endpoint +// bUH_EP_RX_EN & bUH_EP_RBUF_MOD: USB host IN endpoint buffer mode, buffer start address is UH_RX_DMA +// 0 x: disable endpoint and disable buffer +// 1 0: 64 bytes buffer for receiving (IN endpoint) +// 1 1: dual 64 bytes buffer by toggle bit bUH_R_TOG selection for receiving (IN endpoint), total=128bytes + +#define R16_UEP0_DMA (*((PUINT16V)0x40008010)) // endpoint 0 DMA buffer address +#define R16_UEP1_DMA (*((PUINT16V)0x40008014)) // endpoint 1 DMA buffer address +#define R16_UEP2_DMA (*((PUINT16V)0x40008018)) // endpoint 2 DMA buffer address +#define R16_UH_RX_DMA R16_UEP2_DMA // host rx endpoint buffer address +#define R16_UEP3_DMA (*((PUINT16V)0x4000801C)) // endpoint 3 DMA buffer address +#define R16_UH_TX_DMA R16_UEP3_DMA // host tx endpoint buffer address +#define R16_UEP5_DMA (*((PUINT16V)0x40008054)) // endpoint 5 DMA buffer address +#define R16_UEP6_DMA (*((PUINT16V)0x40008058)) // endpoint 6 DMA buffer address +#define R16_UEP7_DMA (*((PUINT16V)0x4000805C)) // endpoint 7 DMA buffer address +#define R32_USB_EP0_CTRL (*((PUINT32V)0x40008020)) // endpoint 0 control & transmittal length +#define R8_UEP0_T_LEN (*((PUINT8V)0x40008020)) // endpoint 0 transmittal length +#define R8_UEP0_CTRL (*((PUINT8V)0x40008022)) // endpoint 0 control +#define R32_USB_EP1_CTRL (*((PUINT32V)0x40008024)) // endpoint 1 control & transmittal length +#define R8_UEP1_T_LEN (*((PUINT8V)0x40008024)) // endpoint 1 transmittal length +#define R8_UEP1_CTRL (*((PUINT8V)0x40008026)) // endpoint 1 control +#define RB_UEP_R_TOG 0x80 // expected data toggle flag of USB endpoint X receiving (OUT): 0=DATA0, 1=DATA1 +#define RB_UEP_T_TOG 0x40 // prepared data toggle flag of USB endpoint X transmittal (IN): 0=DATA0, 1=DATA1 +#define RB_UEP_AUTO_TOG 0x10 // enable automatic toggle after successful transfer completion on endpoint 1/2/3: 0=manual toggle, 1=automatic toggle +#define RB_UEP_R_RES1 0x08 // handshake response type high bit for USB endpoint X receiving (OUT) +#define RB_UEP_R_RES0 0x04 // handshake response type low bit for USB endpoint X receiving (OUT) +#define MASK_UEP_R_RES 0x0C // bit mask of handshake response type for USB endpoint X receiving (OUT) +#define UEP_R_RES_ACK 0x00 +#define UEP_R_RES_TOUT 0x04 +#define UEP_R_RES_NAK 0x08 +#define UEP_R_RES_STALL 0x0C +// RB_UEP_R_RES1 & RB_UEP_R_RES0: handshake response type for USB endpoint X receiving (OUT) +// 00: ACK (ready) +// 01: no response, time out to host, for non-zero endpoint isochronous transactions +// 10: NAK (busy) +// 11: STALL (error) +#define RB_UEP_T_RES1 0x02 // handshake response type high bit for USB endpoint X transmittal (IN) +#define RB_UEP_T_RES0 0x01 // handshake response type low bit for USB endpoint X transmittal (IN) +#define MASK_UEP_T_RES 0x03 // bit mask of handshake response type for USB endpoint X transmittal (IN) +#define UEP_T_RES_ACK 0x00 +#define UEP_T_RES_TOUT 0x01 +#define UEP_T_RES_NAK 0x02 +#define UEP_T_RES_STALL 0x03 +// bUEP_T_RES1 & bUEP_T_RES0: handshake response type for USB endpoint X transmittal (IN) +// 00: DATA0 or DATA1 then expecting ACK (ready) +// 01: DATA0 or DATA1 then expecting no response, time out from host, for non-zero endpoint isochronous transactions +// 10: NAK (busy) +// 11: STALL (error) + +#define R8_UH_SETUP R8_UEP1_CTRL // host aux setup +#define RB_UH_PRE_PID_EN 0x80 // USB host PRE PID enable for low speed device via hub +#define RB_UH_SOF_EN 0x40 // USB host automatic SOF enable + +#define R32_USB_EP2_CTRL (*((PUINT32V)0x40008028)) // endpoint 2 control & transmittal length +#define R8_UEP2_T_LEN (*((PUINT8V)0x40008028)) // endpoint 2 transmittal length +#define R8_UEP2_CTRL (*((PUINT8V)0x4000802A)) // endpoint 2 control + +#define R8_UH_EP_PID R8_UEP2_T_LEN // host endpoint and PID +#define MASK_UH_TOKEN 0xF0 // bit mask of token PID for USB host transfer +#define MASK_UH_ENDP 0x0F // bit mask of endpoint number for USB host transfer + +#define R8_UH_RX_CTRL R8_UEP2_CTRL // host receiver endpoint control +#define RB_UH_R_TOG 0x80 // expected data toggle flag of host receiving (IN): 0=DATA0, 1=DATA1 +#define RB_UH_R_AUTO_TOG 0x10 // enable automatic toggle after successful transfer completion: 0=manual toggle, 1=automatic toggle +#define RB_UH_R_RES 0x04 // prepared handshake response type for host receiving (IN): 0=ACK (ready), 1=no response, time out to device, for isochronous transactions + +#define R32_USB_EP3_CTRL (*((PUINT32V)0x4000802c)) // endpoint 3 control & transmittal length +#define R8_UEP3_T_LEN (*((PUINT8V)0x4000802c)) // endpoint 3 transmittal length +#define R8_UEP3_CTRL (*((PUINT8V)0x4000802e)) // endpoint 3 control +#define R8_UH_TX_LEN R8_UEP3_T_LEN // host transmittal endpoint transmittal length + +#define R8_UH_TX_CTRL R8_UEP3_CTRL // host transmittal endpoint control +#define RB_UH_T_TOG 0x40 // prepared data toggle flag of host transmittal (SETUP/OUT): 0=DATA0, 1=DATA1 +#define RB_UH_T_AUTO_TOG 0x10 // enable automatic toggle after successful transfer completion: 0=manual toggle, 1=automatic toggle +#define RB_UH_T_RES 0x01 // expected handshake response type for host transmittal (SETUP/OUT): 0=ACK (ready), 1=no response, time out from device, for isochronous transactions + +#define R32_USB_EP4_CTRL (*((PUINT32V)0x40008030)) // endpoint 4 control & transmittal length +#define R8_UEP4_T_LEN (*((PUINT8V)0x40008030)) // endpoint 4 transmittal length +#define R8_UEP4_CTRL (*((PUINT8V)0x40008032)) // endpoint 4 control + +#define R32_USB_EP5_CTRL (*((PUINT32V)0x40008064)) // endpoint 5 control & transmittal length +#define R8_UEP5_T_LEN (*((PUINT8V)0x40008064)) // endpoint 5 transmittal length +#define R8_UEP5_CTRL (*((PUINT8V)0x40008066)) // endpoint 5 control + +#define R32_USB_EP6_CTRL (*((PUINT32V)0x40008068)) // endpoint 6 control & transmittal length +#define R8_UEP6_T_LEN (*((PUINT8V)0x40008068)) // endpoint 6 transmittal length +#define R8_UEP6_CTRL (*((PUINT8V)0x4000806A)) // endpoint 6 control + +#define R32_USB_EP7_CTRL (*((PUINT32V)0x4000806C)) // endpoint 7 control & transmittal length +#define R8_UEP7_T_LEN (*((PUINT8V)0x4000806C)) // endpoint 7 transmittal length +#define R8_UEP7_CTRL (*((PUINT8V)0x4000806E)) // endpoint 7 control + +/* USB2 */ +#define R32_USB2_CONTROL (*((PUINT32V)0x40008400)) // USB2 control & interrupt enable & device address +#define R8_USB2_CTRL (*((PUINT8V)0x40008400)) // USB2 base control +#define R8_U2DEV_CTRL (*((PUINT8V)0x40008401)) // USB2 device physical prot control +#define R8_U2HOST_CTRL R8_U2DEV_CTRL // USB2 host physical prot control +#define R8_USB2_INT_EN (*((PUINT8V)0x40008402)) // USB2 interrupt enable +#define R8_USB2_DEV_AD (*((PUINT8V)0x40008403)) // USB2 device address +#define R32_USB2_STATUS (*((PUINT32V)0x40008404)) // USB2 miscellaneous status & interrupt flag & interrupt status +#define R8_USB2_MIS_ST (*((PUINT8V)0x40008405)) // USB2 miscellaneous status +#define R8_USB2_INT_FG (*((PUINT8V)0x40008406)) // USB2 interrupt flag +#define R8_USB2_INT_ST (*((PUINT8V)0x40008407)) // USB2 interrupt status +#define R8_USB2_RX_LEN (*((PUINT8V)0x40008408)) // USB2 receiving length +#define R32_USB2_BUF_MODE (*((PUINT32V)0x4000840C)) // USB2 endpoint buffer mode +#define R8_U2EP4_1_MOD (*((PUINT8V)0x4000840C)) // USB2 endpoint 4/1 mode +#define R8_U2EP2_3_MOD (*((PUINT8V)0x4000840D)) // USB2 endpoint 2/3 mode +#define R8_U2EP567_MOD (*((PUINT8V)0x4000840E)) // USB2 endpoint 5/6/7 mode +#define R8_U2H_EP_MOD R8_U2EP2_3_MOD // USB2 host endpoint mode +#define R16_U2EP0_DMA (*((PUINT16V)0x40008410)) // USB2 endpoint 0 DMA buffer address +#define R16_U2EP1_DMA (*((PUINT16V)0x40008414)) // USB2 endpoint 1 DMA buffer address +#define R16_U2EP2_DMA (*((PUINT16V)0x40008418)) // USB2 endpoint 2 DMA buffer address +#define R16_U2H_RX_DMA R16_U2EP2_DMA // USB2 host rx endpoint buffer address +#define R16_U2EP3_DMA (*((PUINT16V)0x4000841C)) // USB2 endpoint 3 DMA buffer address +#define R16_U2H_TX_DMA R16_U2EP3_DMA // USB2 host tx endpoint buffer address +#define R16_U2EP5_DMA (*((PUINT16V)0x40008454)) // USB2 endpoint 5 DMA buffer address +#define R16_U2EP6_DMA (*((PUINT16V)0x40008458)) // USB2 endpoint 6 DMA buffer address +#define R16_U2EP7_DMA (*((PUINT16V)0x4000845C)) // USB2 endpoint 7 DMA buffer address +#define R32_USB2_EP0_CTRL (*((PUINT32V)0x40008420)) // USB2 endpoint 0 control & transmittal length +#define R8_U2EP0_T_LEN (*((PUINT8V)0x40008420)) // USB2 endpoint 0 transmittal length +#define R8_U2EP0_CTRL (*((PUINT8V)0x40008422)) // USB2 endpoint 0 control +#define R32_USB2_EP1_CTRL (*((PUINT32V)0x40008424)) // USB2 endpoint 1 control & transmittal length +#define R8_U2EP1_T_LEN (*((PUINT8V)0x40008424)) // USB2 endpoint 1 transmittal length +#define R8_U2EP1_CTRL (*((PUINT8V)0x40008426)) // USB2 endpoint 1 control +#define R8_U2H_SETUP R8_U2EP1_CTRL // USB2 host aux setup +#define R32_USB2_EP2_CTRL (*((PUINT32V)0x40008428)) // USB2 endpoint 2 control & transmittal length +#define R8_U2EP2_T_LEN (*((PUINT8V)0x40008428)) // USB2 endpoint 2 transmittal length +#define R8_U2EP2_CTRL (*((PUINT8V)0x4000842A)) // USB2 endpoint 2 control +#define R8_U2H_EP_PID R8_U2EP2_T_LEN // USB2 host endpoint and PID +#define R8_U2H_RX_CTRL R8_U2EP2_CTRL // USB2 host receiver endpoint control +#define R32_USB2_EP3_CTRL (*((PUINT32V)0x4000842c)) // USB2 endpoint 3 control & transmittal length +#define R8_U2EP3_T_LEN (*((PUINT8V)0x4000842c)) // USB2 endpoint 3 transmittal length +#define R8_U2EP3_CTRL (*((PUINT8V)0x4000842e)) // USB2 endpoint 3 control +#define R8_U2H_TX_LEN R8_U2EP3_T_LEN // USB2 host transmittal endpoint transmittal length +#define R8_U2H_TX_CTRL R8_U2EP3_CTRL // USB2 host transmittal endpoint control +#define R32_USB2_EP4_CTRL (*((PUINT32V)0x40008430)) // USB2 endpoint 4 control & transmittal length +#define R8_U2EP4_T_LEN (*((PUINT8V)0x40008430)) // USB2 endpoint 4 transmittal length +#define R8_U2EP4_CTRL (*((PUINT8V)0x40008432)) // USB2 endpoint 4 control +#define R32_USB2_EP5_CTRL (*((PUINT32V)0x40008464)) // USB2 endpoint 5 control & transmittal length +#define R8_U2EP5_T_LEN (*((PUINT8V)0x40008464)) // USB2 endpoint 5 transmittal length +#define R8_U2EP5_CTRL (*((PUINT8V)0x40008466)) // USB2 endpoint 5 control +#define R32_USB2_EP6_CTRL (*((PUINT32V)0x40008468)) // USB2 endpoint 6 control & transmittal length +#define R8_U2EP6_T_LEN (*((PUINT8V)0x40008468)) // USB2 endpoint 6 transmittal length +#define R8_U2EP6_CTRL (*((PUINT8V)0x4000846A)) // USB2 endpoint 6 control +#define R32_USB2_EP7_CTRL (*((PUINT32V)0x4000846C)) // USB2 endpoint 7 control & transmittal length +#define R8_U2EP7_T_LEN (*((PUINT8V)0x4000846C)) // USB2 endpoint 7 transmittal length +#define R8_U2EP7_CTRL (*((PUINT8V)0x4000846E)) // USB2 endpoint 7 control + +#ifdef __cplusplus +} +#endif + +#endif //__CH583USBSFR_H__ + + +#ifndef __USB_TYPE__ +#define __USB_TYPE__ + +#ifdef __cplusplus +extern "C" { +#endif + +/*----- USB constant and structure define --------------------------------*/ + +/* USB PID */ +#ifndef USB_PID_SETUP +#define USB_PID_NULL 0x00 /* reserved PID */ +#define USB_PID_SOF 0x05 +#define USB_PID_SETUP 0x0D +#define USB_PID_IN 0x09 +#define USB_PID_OUT 0x01 +#define USB_PID_ACK 0x02 +#define USB_PID_NAK 0x0A +#define USB_PID_STALL 0x0E +#define USB_PID_DATA0 0x03 +#define USB_PID_DATA1 0x0B +#define USB_PID_PRE 0x0C +#endif + +/* USB standard device request code */ +#ifndef USB_GET_DESCRIPTOR +#define USB_GET_STATUS 0x00 +#define USB_CLEAR_FEATURE 0x01 +#define USB_SET_FEATURE 0x03 +#define USB_SET_ADDRESS 0x05 +#define USB_GET_DESCRIPTOR 0x06 +#define USB_SET_DESCRIPTOR 0x07 +#define USB_GET_CONFIGURATION 0x08 +#define USB_SET_CONFIGURATION 0x09 +#define USB_GET_INTERFACE 0x0A +#define USB_SET_INTERFACE 0x0B +#define USB_SYNCH_FRAME 0x0C +#endif + +/* USB hub class request code */ +#ifndef HUB_GET_DESCRIPTOR +#define HUB_GET_STATUS 0x00 +#define HUB_CLEAR_FEATURE 0x01 +#define HUB_GET_STATE 0x02 +#define HUB_SET_FEATURE 0x03 +#define HUB_GET_DESCRIPTOR 0x06 +#define HUB_SET_DESCRIPTOR 0x07 +#endif + +/* USB HID class request code */ +#ifndef HID_GET_REPORT +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B +#endif + +/* Bit define for USB request type */ +#ifndef USB_REQ_TYP_MASK +#define USB_REQ_TYP_IN 0x80 /* control IN, device to host */ +#define USB_REQ_TYP_OUT 0x00 /* control OUT, host to device */ +#define USB_REQ_TYP_READ 0x80 /* control read, device to host */ +#define USB_REQ_TYP_WRITE 0x00 /* control write, host to device */ +#define USB_REQ_TYP_MASK 0x60 /* bit mask of request type */ +#define USB_REQ_TYP_STANDARD 0x00 +#define USB_REQ_TYP_CLASS 0x20 +#define USB_REQ_TYP_VENDOR 0x40 +#define USB_REQ_TYP_RESERVED 0x60 +#define USB_REQ_RECIP_MASK 0x1F /* bit mask of request recipient */ +#define USB_REQ_RECIP_DEVICE 0x00 +#define USB_REQ_RECIP_INTERF 0x01 +#define USB_REQ_RECIP_ENDP 0x02 +#define USB_REQ_RECIP_OTHER 0x03 +#endif + +/* USB request type for hub class request */ +#ifndef HUB_GET_HUB_DESCRIPTOR +#define HUB_CLEAR_HUB_FEATURE 0x20 +#define HUB_CLEAR_PORT_FEATURE 0x23 +#define HUB_GET_BUS_STATE 0xA3 +#define HUB_GET_HUB_DESCRIPTOR 0xA0 +#define HUB_GET_HUB_STATUS 0xA0 +#define HUB_GET_PORT_STATUS 0xA3 +#define HUB_SET_HUB_DESCRIPTOR 0x20 +#define HUB_SET_HUB_FEATURE 0x20 +#define HUB_SET_PORT_FEATURE 0x23 +#endif + +/* Hub class feature selectors */ +#ifndef HUB_PORT_RESET +#define HUB_C_HUB_LOCAL_POWER 0 +#define HUB_C_HUB_OVER_CURRENT 1 +#define HUB_PORT_CONNECTION 0 +#define HUB_PORT_ENABLE 1 +#define HUB_PORT_SUSPEND 2 +#define HUB_PORT_OVER_CURRENT 3 +#define HUB_PORT_RESET 4 +#define HUB_PORT_POWER 8 +#define HUB_PORT_LOW_SPEED 9 +#define HUB_C_PORT_CONNECTION 16 +#define HUB_C_PORT_ENABLE 17 +#define HUB_C_PORT_SUSPEND 18 +#define HUB_C_PORT_OVER_CURRENT 19 +#define HUB_C_PORT_RESET 20 +#endif + +/* USB descriptor type */ +#ifndef USB_DESCR_TYP_DEVICE +#define USB_DESCR_TYP_DEVICE 0x01 +#define USB_DESCR_TYP_CONFIG 0x02 +#define USB_DESCR_TYP_STRING 0x03 +#define USB_DESCR_TYP_INTERF 0x04 +#define USB_DESCR_TYP_ENDP 0x05 +#define USB_DESCR_TYP_QUALIF 0x06 +#define USB_DESCR_TYP_SPEED 0x07 +#define USB_DESCR_TYP_OTG 0x09 +#define USB_DESCR_TYP_HID 0x21 +#define USB_DESCR_TYP_REPORT 0x22 +#define USB_DESCR_TYP_PHYSIC 0x23 +#define USB_DESCR_TYP_CS_INTF 0x24 +#define USB_DESCR_TYP_CS_ENDP 0x25 +#define USB_DESCR_TYP_HUB 0x29 +#endif + +/* USB device class */ +#ifndef USB_DEV_CLASS_HUB +#define USB_DEV_CLASS_RESERVED 0x00 +#define USB_DEV_CLASS_AUDIO 0x01 +#define USB_DEV_CLASS_COMMUNIC 0x02 +#define USB_DEV_CLASS_HID 0x03 +#define USB_DEV_CLASS_MONITOR 0x04 +#define USB_DEV_CLASS_PHYSIC_IF 0x05 +#define USB_DEV_CLASS_POWER 0x06 +#define USB_DEV_CLASS_PRINTER 0x07 +#define USB_DEV_CLASS_STORAGE 0x08 +#define USB_DEV_CLASS_HUB 0x09 +#define USB_DEV_CLASS_VEN_SPEC 0xFF +#endif + +/* USB endpoint type and attributes */ +#ifndef USB_ENDP_TYPE_MASK +#define USB_ENDP_DIR_MASK 0x80 +#define USB_ENDP_ADDR_MASK 0x0F +#define USB_ENDP_TYPE_MASK 0x03 +#define USB_ENDP_TYPE_CTRL 0x00 +#define USB_ENDP_TYPE_ISOCH 0x01 +#define USB_ENDP_TYPE_BULK 0x02 +#define USB_ENDP_TYPE_INTER 0x03 +#endif + +#ifndef USB_DEVICE_ADDR +#define USB_DEVICE_ADDR 0x02 /* ĬϵUSB豸ַ */ +#endif +#ifndef DEFAULT_ENDP0_SIZE +#define DEFAULT_ENDP0_SIZE 8 /* default maximum packet size for endpoint 0 */ +#endif +#ifndef MAX_PACKET_SIZE +#define MAX_PACKET_SIZE 64 /* maximum packet size */ +#endif +#ifndef USB_BO_CBW_SIZE +#define USB_BO_CBW_SIZE 0x1F /* CBWܳ */ +#define USB_BO_CSW_SIZE 0x0D /* ״̬CSWܳ */ +#endif +#ifndef USB_BO_CBW_SIG +#define USB_BO_CBW_SIG 0x43425355 /* CBWʶ־'USBC' */ +#define USB_BO_CSW_SIG 0x53425355 /* ״̬CSWʶ־'USBS' */ +#endif + +#ifndef __PACKED +#define __PACKED __attribute__((packed)) +#endif + +typedef struct __PACKED _USB_SETUP_REQ { + UINT8 bRequestType; + UINT8 bRequest; + UINT16 wValue; + UINT16 wIndex; + UINT16 wLength; +} USB_SETUP_REQ, *PUSB_SETUP_REQ; + +typedef struct __PACKED _USB_DEVICE_DESCR { + UINT8 bLength; + UINT8 bDescriptorType; + UINT16 bcdUSB; + UINT8 bDeviceClass; + UINT8 bDeviceSubClass; + UINT8 bDeviceProtocol; + UINT8 bMaxPacketSize0; + UINT16 idVendor; + UINT16 idProduct; + UINT16 bcdDevice; + UINT8 iManufacturer; + UINT8 iProduct; + UINT8 iSerialNumber; + UINT8 bNumConfigurations; +} USB_DEV_DESCR, *PUSB_DEV_DESCR; + +typedef struct __PACKED _USB_CONFIG_DESCR { + UINT8 bLength; + UINT8 bDescriptorType; + UINT16 wTotalLength; + UINT8 bNumInterfaces; + UINT8 bConfigurationValue; + UINT8 iConfiguration; + UINT8 bmAttributes; + UINT8 MaxPower; +} USB_CFG_DESCR, *PUSB_CFG_DESCR; + +typedef struct __PACKED _USB_INTERF_DESCR { + UINT8 bLength; + UINT8 bDescriptorType; + UINT8 bInterfaceNumber; + UINT8 bAlternateSetting; + UINT8 bNumEndpoints; + UINT8 bInterfaceClass; + UINT8 bInterfaceSubClass; + UINT8 bInterfaceProtocol; + UINT8 iInterface; +} USB_ITF_DESCR, *PUSB_ITF_DESCR; + +typedef struct __PACKED _USB_ENDPOINT_DESCR { + UINT8 bLength; + UINT8 bDescriptorType; + UINT8 bEndpointAddress; + UINT8 bmAttributes; + UINT16 wMaxPacketSize; + UINT8 bInterval; +} USB_ENDP_DESCR, *PUSB_ENDP_DESCR; + +typedef struct __PACKED _USB_CONFIG_DESCR_LONG { + USB_CFG_DESCR cfg_descr; + USB_ITF_DESCR itf_descr; + USB_ENDP_DESCR endp_descr[1]; +} USB_CFG_DESCR_LONG, *PUSB_CFG_DESCR_LONG; + +typedef USB_CFG_DESCR_LONG *PXUSB_CFG_DESCR_LONG; + +typedef struct __PACKED _USB_HUB_DESCR { + UINT8 bDescLength; + UINT8 bDescriptorType; + UINT8 bNbrPorts; + UINT8 wHubCharacteristicsL; + UINT8 wHubCharacteristicsH; + UINT8 bPwrOn2PwrGood; + UINT8 bHubContrCurrent; + UINT8 DeviceRemovable; + UINT8 PortPwrCtrlMask; +} USB_HUB_DESCR, *PUSB_HUB_DESCR; + +typedef USB_HUB_DESCR *PXUSB_HUB_DESCR; + +typedef struct __PACKED _USB_HID_DESCR { + UINT8 bLength; + UINT8 bDescriptorType; + UINT16 bcdHID; + UINT8 bCountryCode; + UINT8 bNumDescriptors; + UINT8 bDescriptorTypeX; + UINT8 wDescriptorLengthL; + UINT8 wDescriptorLengthH; +} USB_HID_DESCR, *PUSB_HID_DESCR; + +typedef USB_HID_DESCR *PXUSB_HID_DESCR; + +typedef struct __PACKED _UDISK_BOC_CBW { /* command of BulkOnly USB-FlashDisk */ + UINT32 mCBW_Sig; + UINT32 mCBW_Tag; + UINT32 mCBW_DataLen; /* uppest byte of data length, always is 0 */ + UINT8 mCBW_Flag; /* transfer direction and etc. */ + UINT8 mCBW_LUN; + UINT8 mCBW_CB_Len; /* length of command block */ + UINT8 mCBW_CB_Buf[16]; /* command block buffer */ +} UDISK_BOC_CBW, *PUDISK_BOC_CBW; + +typedef UDISK_BOC_CBW *PXUDISK_BOC_CBW; + +typedef struct __PACKED _UDISK_BOC_CSW { /* status of BulkOnly USB-FlashDisk */ + UINT32 mCSW_Sig; + UINT32 mCSW_Tag; + UINT32 mCSW_Residue; /* return: remainder bytes */ + UINT8 mCSW_Status; /* return: result status */ +} UDISK_BOC_CSW, *PUDISK_BOC_CSW; + +typedef UDISK_BOC_CSW *PXUDISK_BOC_CSW; + +#ifdef __cplusplus +} +#endif + +#endif // __USB_TYPE__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_adc.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_adc.h new file mode 100644 index 0000000..0654a5e --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_adc.h @@ -0,0 +1,265 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_adc.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#ifndef __CH58x_ADC_H__ +#define __CH58x_ADC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ROM_CFG_TMP_25C 0x7F014 + +/** + * @brief adc single channel define + */ +typedef enum +{ + CH_EXTIN_0 = 0, // ADC ⲿģͨ 0 + CH_EXTIN_1, // ADC ⲿģͨ 1 + CH_EXTIN_2, // ADC ⲿģͨ 2 + CH_EXTIN_3, // ADC ⲿģͨ 3 + CH_EXTIN_4, // ADC ⲿģͨ 4 + CH_EXTIN_5, // ADC ⲿģͨ 5 + CH_EXTIN_6, // ADC ⲿģͨ 6 + CH_EXTIN_7, // ADC ⲿģͨ 7 + CH_EXTIN_8, // ADC ⲿģͨ 8 + CH_EXTIN_9, // ADC ⲿģͨ 9 + CH_EXTIN_10, // ADC ⲿģͨ 10 + CH_EXTIN_11, // ADC ⲿģͨ 11 + CH_EXTIN_12, // ADC ⲿģͨ 12 + CH_EXTIN_13, // ADC ⲿģͨ 13 + + CH_INTE_VBAT = 14, // ADC ڲؼͨ + CH_INTE_VTEMP = 15, // ADC ڲ¶ȴͨ + +} ADC_SingleChannelTypeDef; + +/** + * @brief adc differential channel define + */ +typedef enum +{ + CH_DIFF_0_2 = 0, // ADC ͨ #0-#2 + CH_DIFF_1_3, // ADC ͨ #1-#3 + +} ADC_DiffChannelTypeDef; + +/** + * @brief adc sampling clock + */ +typedef enum +{ + SampleFreq_3_2 = 0, // 3.2M Ƶ + SampleFreq_8, // 8M Ƶ + SampleFreq_5_33, // 5.33M Ƶ + SampleFreq_4, // 4M Ƶ +} ADC_SampClkTypeDef; + +/** + * @brief adc signal PGA + */ +typedef enum +{ + ADC_PGA_1_4 = 0, // -12dB, 1/4 + ADC_PGA_1_2, // -6dB, 1/2 + ADC_PGA_0, // 0dB, 1 + ADC_PGA_2, // 6dB, 2 +} ADC_SignalPGATypeDef; + +/** + * @brief Configuration DMA mode + */ +typedef enum +{ + ADC_Mode_Single = 0, // ģʽ + ADC_Mode_LOOP, // ѭģʽ +} ADC_DMAModeTypeDef; + + +/** + * @brief ADC ͨ + * + * @param d - refer to ADC_SingleChannelTypeDef + */ +#define ADC_ChannelCfg(d) (R8_ADC_CHANNEL = d) + +/** + * @brief ADC ʱ + * + * @param d - refer to ADC_SampClkTypeDef + */ +#define ADC_SampClkCfg(d) (R8_ADC_CFG = R8_ADC_CFG & (~RB_ADC_CLK_DIV) | (d << 6)) + +/** + * @brief ADC ź + * + * @param d - refer to ADC_SignalPGATypeDef + */ +#define ADC_PGACfg(d) (R8_ADC_CFG = R8_ADC_CFG & (~RB_ADC_PGA_GAIN) | (d << 4)) + +/** + * @brief ڲ¶ȴУ׼ֵ + * + * @param d - У׼ֵ + */ +#define ADC_TempCalibCfg(d) (R8_TEM_SENSOR = R8_TEM_SENSOR & (~RB_TEM_SEN_CALIB) | d) + +/** + * @brief ⲿźŵͨʼ + * + * @param sp - refer to ADC_SampClkTypeDef + * @param ga - refer to ADC_SignalPGATypeDef + */ +void ADC_ExtSingleChSampInit(ADC_SampClkTypeDef sp, ADC_SignalPGATypeDef ga); + +/** + * @brief ⲿźŲͨʼ + * + * @param sp - refer to ADC_SampClkTypeDef + * @param ga - refer to ADC_SignalPGATypeDef + */ +void ADC_ExtDiffChSampInit(ADC_SampClkTypeDef sp, ADC_SignalPGATypeDef ga); + +/** + * @brief ͨʼ + */ +void TouchKey_ChSampInit(void); + +/** + * @brief رTouchKeyԴ + */ +#define TouchKey_DisableTSPower() (R8_TKEY_CFG &= ~RB_TKEY_PWR_ON) + +/** + * @brief ¶ȴʼ + */ +void ADC_InterTSSampInit(void); + +/** + * @brief ر¶ȴԴ + */ +#define ADC_DisableTSPower() (R8_TEM_SENSOR = 0) + +/** + * @brief õصѹʼ + */ +void ADC_InterBATSampInit(void); + +/** + * @brief ADCִеת + * + * @return ADCת + */ +uint16_t ADC_ExcutSingleConver(void); + +/** + * @brief ݴֵ,ȡƫֵ,ADCô˺ȡУ׼ֵ + * + * @return ƫ + */ +signed short ADC_DataCalib_Rough(void); + +/** + * @brief TouchKeyת + * + * @param charg - Touchkeyʱ,5bitsЧ, t=charg*Tadc + * @param disch - Touchkeyŵʱ,3bitsЧ, t=disch*Tadc + * + * @return ǰTouchKeyЧ + */ +uint16_t TouchKey_ExcutSingleConver(uint8_t charg, uint8_t disch); + +/** + * @brief ADC + * + * @param cycle - λΪ 16ϵͳʱ + */ +void ADC_AutoConverCycle(uint8_t cycle); + +/** + * @brief DMA + * + * @param s - ǷDMA + * @param startAddr - DMA ʼַ + * @param endAddr - DMA ַ + * @param m - DMAģʽ + */ +void ADC_DMACfg(uint8_t s, uint16_t startAddr, uint16_t endAddr, ADC_DMAModeTypeDef m); + +/** + * @brief Convert ADC value to temperature(Celsius) + * + * @param adc_val - adc value + * + * @return temperature (Celsius) + */ +int adc_to_temperature_celsius(uint16_t adc_val); + +/** + * @brief ȡADCתֵ + * + * @return ADCתֵ + */ +#define ADC_ReadConverValue() (R16_ADC_DATA) + +/** + * @brief ADCִеת + */ +#define ADC_StartUp() (R8_ADC_CONVERT = RB_ADC_START) + +/** + * @brief ȡADCж״̬ + */ +#define ADC_GetITStatus() (R8_ADC_INT_FLAG & RB_ADC_IF_EOC) + +/** + * @brief ADCжϱ־ + */ +#define ADC_ClearITFlag() (R8_ADC_CONVERT = 0) + +/** + * @brief ȡADC DMA״̬ + */ +#define ADC_GetDMAStatus() (R8_ADC_DMA_IF & RB_ADC_IF_DMA_END) + +/** + * @brief ADC DMAɱ־ + */ +#define ADC_ClearDMAFlag() (R8_ADC_DMA_IF |= RB_ADC_IF_DMA_END) + +/** + * @brief Զ ADC + */ +#define ADC_StartDMA() (R8_ADC_CTRL_DMA |= RB_ADC_AUTO_EN) + +/** + * @brief ֹͣԶ ADC + */ +#define ADC_StopDMA() (R8_ADC_CTRL_DMA &= ~RB_ADC_AUTO_EN) + +/** + * @brief ȡTouchKeyж״̬ + */ +#define TouchKey_GetITStatus() (R8_ADC_INT_FLAG & RB_ADC_IF_EOC) + +/** + * @brief TouchKeyжϱ־ + */ +#define TouchKey_ClearITFlag() (R8_TKEY_CTRL |= RB_TKEY_PWR_ON) + + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_ADC_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_clk.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_clk.h new file mode 100644 index 0000000..ed1984d --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_clk.h @@ -0,0 +1,290 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_clk.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#ifndef __CH58x_CLK_H__ +#define __CH58x_CLK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ϵͳƵ + */ +typedef enum +{ + CLK_SOURCE_LSI = 0x00, + CLK_SOURCE_LSE, + + CLK_SOURCE_HSE_16MHz = 0x22, + CLK_SOURCE_HSE_8MHz = 0x24, + CLK_SOURCE_HSE_6_4MHz = 0x25, + CLK_SOURCE_HSE_4MHz = 0x28, + CLK_SOURCE_HSE_2MHz = (0x20 | 16), + CLK_SOURCE_HSE_1MHz = (0x20 | 0), + + CLK_SOURCE_PLL_80MHz = 0x46, + CLK_SOURCE_PLL_60MHz = 0x48, + CLK_SOURCE_PLL_48MHz = (0x40 | 10), + CLK_SOURCE_PLL_40MHz = (0x40 | 12), + CLK_SOURCE_PLL_36_9MHz = (0x40 | 13), + CLK_SOURCE_PLL_32MHz = (0x40 | 15), + CLK_SOURCE_PLL_30MHz = (0x40 | 16), + CLK_SOURCE_PLL_24MHz = (0x40 | 20), + CLK_SOURCE_PLL_20MHz = (0x40 | 24), + CLK_SOURCE_PLL_15MHz = (0x40 | 0), +} SYS_CLKTypeDef; + +/** + * @brief 32Kʱѡ + */ +typedef enum +{ + Clk32K_LSI = 0, + Clk32K_LSE, + +} LClk32KTypeDef; + +/** + * @brief 32Mλ + */ +typedef enum +{ + HSE_RCur_75 = 0, + HSE_RCur_100, + HSE_RCur_125, + HSE_RCur_150 + +} HSECurrentTypeDef; + +/** + * @brief 32Mڲݵλ + */ +typedef enum +{ + HSECap_10p = 0, + HSECap_12p, + HSECap_14p, + HSECap_16p, + HSECap_18p, + HSECap_20p, + HSECap_22p, + HSECap_24p + +} HSECapTypeDef; + +/** + * @brief 32Kλ + */ +typedef enum +{ + LSE_RCur_70 = 0, + LSE_RCur_100, + LSE_RCur_140, + LSE_RCur_200 + +} LSECurrentTypeDef; + +/** + * @brief 32Kڲݵλ + */ +typedef enum +{ + LSECap_2p = 0, + LSECap_13p, + LSECap_14p, + LSECap_15p, + LSECap_16p, + LSECap_17p, + LSECap_18p, + LSECap_19p, + LSECap_20p, + LSECap_21p, + LSECap_22p, + LSECap_23p, + LSECap_24p, + LSECap_25p, + LSECap_26p, + LSECap_27p + +} LSECapTypeDef; + +#define MAX_DAY 0x00004000 +#define MAX_2_SEC 0x0000A8C0 +//#define MAX_SEC 0x545FFFFF + +#define BEGYEAR 2020 +#define IsLeapYear(yr) (!((yr) % 400) || (((yr) % 100) && !((yr) % 4))) +#define YearLength(yr) (IsLeapYear(yr) ? 366 : 365) +#define monthLength(lpyr, mon) (((mon) == 1) ? (28 + (lpyr)) : (((mon) > 6) ? (((mon) & 1) ? 31 : 30) : (((mon) & 1) ? 30 : 31))) + +/** + * @brief rtc timer mode period define + */ +typedef enum +{ + Period_0_125_S = 0, // 0.125s + Period_0_25_S, // 0.25s + Period_0_5_S, // 0.5s + Period_1_S, // 1s + Period_2_S, // 2s + Period_4_S, // 4s + Period_8_S, // 8s + Period_16_S, // 16s +} RTC_TMRCycTypeDef; + +/** + * @brief rtc interrupt event define + */ +typedef enum +{ + RTC_TRIG_EVENT = 0, // RTC ¼ + RTC_TMR_EVENT, // RTC ڶʱ¼ + +} RTC_EVENTTypeDef; + +/** + * @brief rtc interrupt event define + */ +typedef enum +{ + RTC_TRIG_MODE = 0, // RTC ģʽ + RTC_TMR_MODE, // RTC ڶʱģʽ + +} RTC_MODETypeDef; + +typedef enum +{ + /* У׼ԽߣʱԽ */ + Level_32 = 3, // ʱ 1.2ms 1000ppm (32M Ƶ) 1100ppm (60M Ƶ) + Level_64, // ʱ 2.2ms 800ppm (32M Ƶ) 1000ppm (60M Ƶ) + Level_128, // ʱ 4.2ms 600ppm (32M Ƶ) 800ppm (60M Ƶ) + +} Cali_LevelTypeDef; + +/** + * @brief 32K ƵʱԴ + * + * @param hc - ѡ32Kʹڲⲿ + */ +void LClk32K_Select(LClk32KTypeDef hc); + +/** + * @brief HSE ƫõ + * + * @param c - 75%,100%,125%,150% + */ +void HSECFG_Current(HSECurrentTypeDef c); + +/** + * @brief HSE ص + * + * @param c - refer to HSECapTypeDef + */ +void HSECFG_Capacitance(HSECapTypeDef c); + +/** + * @brief LSE ƫõ + * + * @param c - 70%,100%,140%,200% + */ +void LSECFG_Current(LSECurrentTypeDef c); + +/** + * @brief LSE ص + * + * @param c - refer to LSECapTypeDef + */ +void LSECFG_Capacitance(LSECapTypeDef c); + +void Calibration_LSI(Cali_LevelTypeDef cali_Lv); /* ƵУ׼ڲ32Kʱ */ + +/** + * @brief RTCʱӳʼǰʱ + * + * @param y - ꣬MAX_Y = BEGYEAR + 44 + * @param mon - £MAX_MON = 12 + * @param d - գMAX_D = 31 + * @param h - СʱMAX_H = 23 + * @param m - ÷ӣMAX_M = 59 + * @param s - 룬MAX_S = 59 + */ +void RTC_InitTime(uint16_t y, uint16_t mon, uint16_t d, uint16_t h, uint16_t m, uint16_t s); + +/** + * @brief ȡǰʱ + * + * @param py - ȡ꣬MAX_Y = BEGYEAR + 44 + * @param pmon - ȡ£MAX_MON = 12 + * @param pd - ȡգMAX_D = 31 + * @param ph - ȡСʱMAX_H = 23 + * @param pm - ȡķӣMAX_M = 59 + * @param ps - ȡ룬MAX_S = 59 + */ +void RTC_GetTime(uint16_t *py, uint16_t *pmon, uint16_t *pd, uint16_t *ph, uint16_t *pm, uint16_t *ps); + +/** + * @brief LSE/LSIʱӣõǰRTC + * + * @param cyc - ڼֵMAX_CYC = 0xA8BFFFFF = 2831155199 + */ +void RTC_SetCycle32k(uint32_t cyc); + +/** + * @brief LSE/LSIʱӣȡǰRTC + * + * @return ǰMAX_CYC = 0xA8BFFFFF = 2831155199 + */ +uint32_t RTC_GetCycle32k(void); + +/** + * @brief RTCʱģʽãעⶨʱ׼̶Ϊ32768Hz + * + * @param t - refer to RTC_TMRCycTypeDef + */ +void RTC_TRIGFunCfg(uint32_t cyc); + +/** + * @brief RTCʱģʽãעⶨʱ׼̶Ϊ32768Hz + * + * @param t - refer to RTC_TMRCycTypeDef + */ +void RTC_TMRFunCfg(RTC_TMRCycTypeDef t); + +/** + * @brief RTC ģʽܹر + * + * @param m - Ҫرյĵǰģʽ + */ +void RTC_ModeFunDisable(RTC_MODETypeDef m); + +/** + * @brief ȡRTCжϱ־ + * + * @param f - refer to RTC_EVENTTypeDef + * + * @return жϱ־״̬ + */ +uint8_t RTC_GetITFlag(RTC_EVENTTypeDef f); + +/** + * @brief RTCжϱ־ + * + * @param f - refer to RTC_EVENTTypeDef + */ +void RTC_ClearITFlag(RTC_EVENTTypeDef f); + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_CLK_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_common.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_common.h new file mode 100644 index 0000000..b51fe14 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_common.h @@ -0,0 +1,91 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_common.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + + +#ifndef __CH58x_COMM_H__ +#define __CH58x_COMM_H__ + +#ifdef __cplusplus + extern "C" { +#endif + + +#ifndef NULL +#define NULL 0 +#endif +#define ALL 0xFFFF + +#ifndef __HIGH_CODE +#define __HIGH_CODE __attribute__((section(".highcode"))) +#endif + +#ifndef __INTERRUPT +#ifdef INT_SOFT +#define __INTERRUPT __attribute__((interrupt())) +#else +#define __INTERRUPT __attribute__((interrupt("WCH-Interrupt-fast"))) +#endif +#endif + +/** + * @brief ϵͳƵʱӣHz + */ +#ifndef FREQ_SYS +#define FREQ_SYS 60000000 +#endif + +#ifndef SAFEOPERATE +#define SAFEOPERATE __nop();__nop() +#endif + +/** + * @brief 32KʱӣHz + */ +#ifdef CLK_OSC32K +#if ( CLK_OSC32K == 1 ) +#define CAB_LSIFQ 32000 +#else +#define CAB_LSIFQ 32768 +#endif +#else +#define CAB_LSIFQ 32000 +#endif + +#include +#include +#include "CH583SFR.h" +#include "core_riscv.h" +#include "CH58x_clk.h" +#include "CH58x_uart.h" +#include "CH58x_gpio.h" +#include "CH58x_i2c.h" +#include "CH58x_flash.h" +#include "CH58x_pwr.h" +#include "CH58x_pwm.h" +#include "CH58x_adc.h" +#include "CH58x_sys.h" +#include "CH58x_timer.h" +#include "CH58x_spi.h" +#include "CH58x_usbhost.h" +#include "ISP583.h" + + +#define DelayMs(x) mDelaymS(x) +#define DelayUs(x) mDelayuS(x) + + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_COMM_H__ + diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_flash.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_flash.h new file mode 100644 index 0000000..bfbe28c --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_flash.h @@ -0,0 +1,42 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_flash.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#ifndef __CH58x_FLASH_H__ +#define __CH58x_FLASH_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ȡFlash-ROM + * + * @param StartAddr - read address + * @param Buffer - read buffer + * @param len - read len + */ +void FLASH_ROM_READ(uint32_t StartAddr, void *Buffer, uint32_t len); + +void FLASH_ROM_READ(UINT32 StartAddr, PVOID Buffer, UINT32 len); /* ȡFlash-ROM */ + +UINT8 UserOptionByteConfig(FunctionalState RESET_EN, FunctionalState BOOT_PIN, FunctionalState UART_NO_KEY_EN, + UINT32 FLASHProt_Size); + +UINT8 UserOptionByteClose_SWD(void); + +void UserOptionByte_Active(void); + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_FLASH_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_gpio.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_gpio.h new file mode 100644 index 0000000..f5072ce --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_gpio.h @@ -0,0 +1,274 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_gpio.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#ifndef __CH58x_GPIO_H__ +#define __CH58x_GPIO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief GPIO_pins_define + */ +#define GPIO_Pin_0 (0x00000001) /*!< Pin 0 selected */ +#define GPIO_Pin_1 (0x00000002) /*!< Pin 1 selected */ +#define GPIO_Pin_2 (0x00000004) /*!< Pin 2 selected */ +#define GPIO_Pin_3 (0x00000008) /*!< Pin 3 selected */ +#define GPIO_Pin_4 (0x00000010) /*!< Pin 4 selected */ +#define GPIO_Pin_5 (0x00000020) /*!< Pin 5 selected */ +#define GPIO_Pin_6 (0x00000040) /*!< Pin 6 selected */ +#define GPIO_Pin_7 (0x00000080) /*!< Pin 7 selected */ +#define GPIO_Pin_8 (0x00000100) /*!< Pin 8 selected */ +#define GPIO_Pin_9 (0x00000200) /*!< Pin 9 selected */ +#define GPIO_Pin_10 (0x00000400) /*!< Pin 10 selected */ +#define GPIO_Pin_11 (0x00000800) /*!< Pin 11 selected */ +#define GPIO_Pin_12 (0x00001000) /*!< Pin 12 selected */ +#define GPIO_Pin_13 (0x00002000) /*!< Pin 13 selected */ +#define GPIO_Pin_14 (0x00004000) /*!< Pin 14 selected */ +#define GPIO_Pin_15 (0x00008000) /*!< Pin 15 selected */ +#define GPIO_Pin_16 (0x00010000) /*!< Pin 16 selected */ +#define GPIO_Pin_17 (0x00020000) /*!< Pin 17 selected */ +#define GPIO_Pin_18 (0x00040000) /*!< Pin 18 selected */ +#define GPIO_Pin_19 (0x00080000) /*!< Pin 19 selected */ +#define GPIO_Pin_20 (0x00100000) /*!< Pin 20 selected */ +#define GPIO_Pin_21 (0x00200000) /*!< Pin 21 selected */ +#define GPIO_Pin_22 (0x00400000) /*!< Pin 22 selected */ +#define GPIO_Pin_23 (0x00800000) /*!< Pin 23 selected */ +#define GPIO_Pin_All (0xFFFFFFFF) /*!< All pins selected */ + +/** + * @brief Configuration GPIO Mode + */ +typedef enum +{ + GPIO_ModeIN_Floating, // + GPIO_ModeIN_PU, // + GPIO_ModeIN_PD, // + GPIO_ModeOut_PP_5mA, //5mA + GPIO_ModeOut_PP_20mA, //20mA + +} GPIOModeTypeDef; + +/** + * @brief Configuration GPIO IT Mode + */ +typedef enum +{ + GPIO_ITMode_LowLevel, //͵ƽ + GPIO_ITMode_HighLevel, //ߵƽ + GPIO_ITMode_FallEdge, //½ش + GPIO_ITMode_RiseEdge, //ش + +} GPIOITModeTpDef; + +/** + * @brief GPIOA˿ģʽ + * + * @param pin - PA0-PA15 + * @param mode - + */ +void GPIOA_ModeCfg(uint32_t pin, GPIOModeTypeDef mode); + +/** + * @brief GPIOB˿ģʽ + * + * @param pin - PB0-PB23 + * @param mode - + */ +void GPIOB_ModeCfg(uint32_t pin, GPIOModeTypeDef mode); + +/** + * @brief GPIOA˿õ + * + * @param pin - PA0-PA15 + */ +#define GPIOA_ResetBits(pin) (R32_PA_CLR |= pin) + +/** + * @brief GPIOA˿ø + * + * @param pin - PA0-PA15 + */ +#define GPIOA_SetBits(pin) (R32_PA_OUT |= pin) + +/** + * @brief GPIOB˿õ + * + * @param pin - PB0-PB23 + */ +#define GPIOB_ResetBits(pin) (R32_PB_CLR |= pin) + +/** + * @brief GPIOB˿ø + * + * @param pin - PB0-PB23 + */ +#define GPIOB_SetBits(pin) (R32_PB_OUT |= pin) + +/** + * @brief GPIOA˿ƽת + * + * @param pin - PA0-PA15 + */ +#define GPIOA_InverseBits(pin) (R32_PA_OUT ^= pin) + +/** + * @brief GPIOB˿ƽת + * + * @param pin - PB0-PB23 + */ +#define GPIOB_InverseBits(pin) (R32_PB_OUT ^= pin) + +/** + * @brief GPIOA˿32λݷأ16λЧ + * + * @return GPIOA˿32λ + */ +#define GPIOA_ReadPort() (R32_PA_PIN) + +/** + * @brief GPIOB˿32λݷأ24λЧ + * + * @return GPIOB˿32λ + */ +#define GPIOB_ReadPort() (R32_PB_PIN) + +/** + * @brief GPIOA˿״̬0-ŵ͵ƽ(!0)-Ÿߵƽ + * + * @param pin - PA0-PA15 + * + * @return GPIOA˿״̬ + */ +#define GPIOA_ReadPortPin(pin) (R32_PA_PIN & (pin)) + +/** + * @brief GPIOB˿״̬0-ŵ͵ƽ(!0)-Ÿߵƽ + * + * @param pin - PB0-PB23 + * + * @return GPIOB˿״̬ + */ +#define GPIOB_ReadPortPin(pin) (R32_PB_PIN & (pin)) + +/** + * @brief GPIOAжģʽ + * + * @param pin - PA0-PA15 + * @param mode - + */ +void GPIOA_ITModeCfg(uint32_t pin, GPIOITModeTpDef mode); + +/** + * @brief GPIOBжģʽ + * + * @param pin - PB0-PB23 + * @param mode - + */ +void GPIOB_ITModeCfg(uint32_t pin, GPIOITModeTpDef mode); + +/** + * @brief ȡGPIOA˿жϱ־״̬ + * + * @return GPIOA˿жϱ־״̬ + */ +#define GPIOA_ReadITFlagPort() (R16_PA_INT_IF) + +/** + * @brief ȡGPIOB˿жϱ־״̬ + * + * @return GPIOB˿жϱ־״̬ + */ +#define GPIOB_ReadITFlagPort() ((R16_PB_INT_IF & (~((GPIO_Pin_22 | GPIO_Pin_23) >> 14))) | ((R16_PB_INT_IF << 14) & (GPIO_Pin_22 | GPIO_Pin_23))) + +/** + * @brief ȡGPIOA˿жϱ־״̬ + * + * @param pin - PA0-PA15 + * + * @return GPIOA˿жϱ־״̬ + */ +#define GPIOA_ReadITFlagBit(pin) (R16_PA_INT_IF & (pin)) + +/** + * @brief ȡGPIOB˿жϱ־״̬ + * + * @param pin - PB0-PB23 + * + * @return GPIOB˿жϱ־״̬ + */ +#define GPIOB_ReadITFlagBit(pin) (R16_PB_INT_IF & ((pin) | (((pin) & (GPIO_Pin_22 | GPIO_Pin_23)) >> 14))) + +/** + * @brief GPIOA˿жϱ־״̬ + * + * @param pin - PA0-PA15 + */ +#define GPIOA_ClearITFlagBit(pin) (R16_PA_INT_IF = pin) + +/** + * @brief GPIOB˿жϱ־״̬ + * + * @param pin - PB0-PB23 + */ +#define GPIOB_ClearITFlagBit(pin) (R16_PB_INT_IF = ((pin) | (((pin) & (GPIO_Pin_22 | GPIO_Pin_23)) >> 14))) + +/** + * @brief 蹦ӳ + * + * @param s - Ƿʹӳ + * @param perph - RB_RF_ANT_SW_EN - RF antenna switch control output on PB16/PB17/PB18/PB19/PB20/PB21 + * RB_PIN_U0_INV - RXD0/RXD0_/TXD0/TXD0_ invert input/output + * RB_PIN_INTX - INTX: INT24/INT25 PB8/PB9 -> INT24_/INT25_ PB22/PB23 + * RB_PIN_MODEM - MODEM: PB1/PB5 -> PB14/PB15 + * RB_PIN_I2C - I2C: PB13/PB12 -> PB21/PB20 + * RB_PIN_PWMX - PWMX: PA12/PA13/PB4/PB6/PB7 -> PA6/PA7/PB1/PB2/PB3 + * RB_PIN_SPI0 - SPI0: PA12/PA13/PA14/PA15 -> PB12/PB13/PB14/PB15 + * RB_PIN_UART3 - UART3: PA4/PA5 -> PB20/PB21 + * RB_PIN_UART2 - UART2: PA6/PA7 -> PB22/PB23 + * RB_PIN_UART1 - UART1: PA8/PA9 -> PB12/PB13 + * RB_PIN_UART0 - UART0: PB4/PB7 -> PA15/PA14 + * RB_PIN_TMR3 - TMR2: PA9 -> PB23 + * RB_PIN_TMR2 - TMR2: PA11 -> PB11 + * RB_PIN_TMR1 - TMR1: PA10 -> PB10 + * RB_PIN_TMR0 - TMR0: PA9 -> PB23 + */ +void GPIOPinRemap(FunctionalState s, uint16_t perph); + +/** + * @brief ģGPIOŹܿ + * + * @param s - Ƿģ蹦 + * @param perph - RB_PIN_ADC8_9_IE - ADC/TKEY 9/8ͨ + * RB_PIN_ADC6_7_IE - ADC/TKEY 7/6ͨ + * RB_PIN_ADC10_IE - ADC/TKEY 10ͨ + * RB_PIN_ADC11_IE - ADC/TKEY 11 ͨ + * RB_PIN_USB2_DP_PU - USB2 U2D+ڲ + * RB_PIN_USB2_IE - USB2 + * RB_PIN_USB_DP_PU - USB UD+ڲ + * RB_PIN_USB_IE - USB + * RB_PIN_ADC0_IE - ADC/TKEY 0 ͨ + * RB_PIN_ADC1_IE - ADC/TKEY 1 ͨ + * RB_PIN_ADC12_IE - ADC/TKEY 12 ͨ + * RB_PIN_ADC13_IE - ADC/TKEY 13 ͨ + * RB_PIN_XT32K_IE - 32KHzLSE + * RB_PIN_ADC2_3_IE - ADC/TKEY 2/3 ͨ + * RB_PIN_ADC4_5_IE - ADC/TKEY 4/5 ͨ + */ +void GPIOAGPPCfg(FunctionalState s, uint16_t perph); + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_GPIO_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_i2c.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_i2c.h new file mode 100644 index 0000000..1f30ced --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_i2c.h @@ -0,0 +1,180 @@ + + +#ifndef __CH58x_I2C_H__ +#define __CH58x_I2C_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* I2C_transfer_direction */ +#define I2C_Direction_Transmitter ((uint8_t)0x00) +#define I2C_Direction_Receiver ((uint8_t)0x01) + +/* I2C ADD0 mask */ +#define OADDR1_ADD0_Set ((uint16_t)0x0001) +#define OADDR1_ADD0_Reset ((uint16_t)0xFFFE) + +/* I2C_NACK_position */ +#define I2C_NACKPosition_Next ((uint16_t)RB_I2C_POS) +#define I2C_NACKPosition_Current ((uint16_t)~RB_I2C_POS) + +/* I2C_PEC_position */ +#define I2C_PECPosition_Next ((uint16_t)RB_I2C_POS) +#define I2C_PECPosition_Current ((uint16_t)~RB_I2C_POS) + +/* I2C_SMBus_alert_pin_level */ +#define I2C_SMBusAlert_Low ((uint16_t)RB_I2C_ALERT) +#define I2C_SMBusAlert_High ((uint16_t)~RB_I2C_ALERT) + +/* I2C FLAG mask */ +#define FLAG_Mask ((uint32_t)0x00FFFFFF) + +/* I2C Interrupt Enable mask */ +#define ITEN_Mask ((uint32_t)0x07000000) + +/* I2C_mode */ +typedef enum +{ + I2C_Mode_I2C = 0x0000, + I2C_Mode_SMBusDevice = 0x0002, + I2C_Mode_SMBusHost = 0x000A, +} I2C_ModeTypeDef; + +/* I2C_duty_cycle_in_fast_mode */ +typedef enum +{ + I2C_DutyCycle_16_9 = RB_I2C_DUTY, /* I2C fast mode Tlow/Thigh = 16/9 */ + I2C_DutyCycle_2 = 0x0000, /* I2C fast mode Tlow/Thigh = 2 */ +} I2C_DutyTypeDef; + +/* I2C_acknowledgement - Enables or disables the acknowledgement.*/ +typedef enum +{ + I2C_Ack_Enable = RB_I2C_ACK, + I2C_Ack_Disable = 0x0000, +} I2C_AckTypeDef; + +/* I2C_acknowledged_address - Specifies if 7-bit or 10-bit address is acknowledged. */ +typedef enum +{ + I2C_AckAddr_7bit = 0x4000, + I2C_AckAddr_10bit = 0xC000, +} I2C_AckAddrTypeDef; + +/* I2C_interrupts_definition */ +typedef enum +{ + I2C_IT_BUF = 0x0400, /* Buffer interrupt mask. */ + I2C_IT_EVT = 0x0200, /* Event interrupt mask. */ + I2C_IT_ERR = 0x0100, /* Error interrupt mask. */ +} I2C_ITTypeDef; + +/* I2C_interrupts_definition */ +#define I2C_IT_SMBALERT ((uint32_t)0x01008000) +#define I2C_IT_TIMEOUT ((uint32_t)0x01004000) +#define I2C_IT_PECERR ((uint32_t)0x01001000) +#define I2C_IT_OVR ((uint32_t)0x01000800) +#define I2C_IT_AF ((uint32_t)0x01000400) +#define I2C_IT_ARLO ((uint32_t)0x01000200) +#define I2C_IT_BERR ((uint32_t)0x01000100) +#define I2C_IT_TXE ((uint32_t)0x06000080) +#define I2C_IT_RXNE ((uint32_t)0x06000040) +#define I2C_IT_STOPF ((uint32_t)0x02000010) +#define I2C_IT_ADD10 ((uint32_t)0x02000008) +#define I2C_IT_BTF ((uint32_t)0x02000004) +#define I2C_IT_ADDR ((uint32_t)0x02000002) +#define I2C_IT_SB ((uint32_t)0x02000001) + +/* SR2 register flags */ +#define I2C_FLAG_DUALF ((uint32_t)0x00800000) +#define I2C_FLAG_SMBHOST ((uint32_t)0x00400000) +#define I2C_FLAG_SMBDEFAULT ((uint32_t)0x00200000) +#define I2C_FLAG_GENCALL ((uint32_t)0x00100000) +#define I2C_FLAG_TRA ((uint32_t)0x00040000) +#define I2C_FLAG_BUSY ((uint32_t)0x00020000) +#define I2C_FLAG_MSL ((uint32_t)0x00010000) + +/* SR1 register flags */ +#define I2C_FLAG_SMBALERT ((uint32_t)0x10008000) +#define I2C_FLAG_TIMEOUT ((uint32_t)0x10004000) +#define I2C_FLAG_PECERR ((uint32_t)0x10001000) +#define I2C_FLAG_OVR ((uint32_t)0x10000800) +#define I2C_FLAG_AF ((uint32_t)0x10000400) +#define I2C_FLAG_ARLO ((uint32_t)0x10000200) +#define I2C_FLAG_BERR ((uint32_t)0x10000100) +#define I2C_FLAG_TXE ((uint32_t)0x10000080) +#define I2C_FLAG_RXNE ((uint32_t)0x10000040) +#define I2C_FLAG_STOPF ((uint32_t)0x10000010) +#define I2C_FLAG_ADD10 ((uint32_t)0x10000008) +#define I2C_FLAG_BTF ((uint32_t)0x10000004) +#define I2C_FLAG_ADDR ((uint32_t)0x10000002) +#define I2C_FLAG_SB ((uint32_t)0x10000001) + +/****************I2C Master Events (Events grouped in order of communication)********************/ + +#define I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */ +#define I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */ +#define I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ((uint32_t)0x00030002) /* BUSY, MSL and ADDR flags */ +#define I2C_EVENT_MASTER_MODE_ADDRESS10 ((uint32_t)0x00030008) /* BUSY, MSL and ADD10 flags */ +#define I2C_EVENT_MASTER_BYTE_RECEIVED ((uint32_t)0x00030040) /* BUSY, MSL and RXNE flags */ +#define I2C_EVENT_MASTER_BYTE_TRANSMITTING ((uint32_t)0x00070080) /* TRA, BUSY, MSL, TXE flags */ +#define I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */ + +/******************I2C Slave Events (Events grouped in order of communication)******************/ + +#define I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED ((uint32_t)0x00020002) /* BUSY and ADDR flags */ +#define I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED ((uint32_t)0x00060082) /* TRA, BUSY, TXE and ADDR flags */ +#define I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED ((uint32_t)0x00820000) /* DUALF and BUSY flags */ +#define I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED ((uint32_t)0x00860080) /* DUALF, TRA, BUSY and TXE flags */ +#define I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED ((uint32_t)0x00120000) /* GENCALL and BUSY flags */ +#define I2C_EVENT_SLAVE_BYTE_RECEIVED ((uint32_t)0x00020040) /* BUSY and RXNE flags */ +#define I2C_EVENT_SLAVE_STOP_DETECTED ((uint32_t)0x00000010) /* STOPF flag */ +#define I2C_EVENT_SLAVE_BYTE_TRANSMITTED ((uint32_t)0x00060084) /* TRA, BUSY, TXE and BTF flags */ +#define I2C_EVENT_SLAVE_BYTE_TRANSMITTING ((uint32_t)0x00060080) /* TRA, BUSY and TXE flags */ +#define I2C_EVENT_SLAVE_ACK_FAILURE ((uint32_t)0x00000400) /* AF flag */ + +void I2C_Init(I2C_ModeTypeDef I2C_Mode, UINT32 I2C_ClockSpeed, I2C_DutyTypeDef I2C_DutyCycle, + I2C_AckTypeDef I2C_Ack, I2C_AckAddrTypeDef I2C_AckAddr, UINT16 I2C_OwnAddress1); +void I2C_Cmd(FunctionalState NewState); +void I2C_GenerateSTART(FunctionalState NewState); +void I2C_GenerateSTOP(FunctionalState NewState); +void I2C_AcknowledgeConfig(FunctionalState NewState); +void I2C_OwnAddress2Config(uint8_t Address); +void I2C_DualAddressCmd(FunctionalState NewState); +void I2C_GeneralCallCmd(FunctionalState NewState); +void I2C_ITConfig(I2C_ITTypeDef I2C_IT, FunctionalState NewState); +void I2C_SendData(uint8_t Data); + +uint8_t I2C_ReceiveData(void); + +void I2C_Send7bitAddress(uint8_t Address, uint8_t I2C_Direction); +void I2C_SoftwareResetCmd(FunctionalState NewState); +void I2C_NACKPositionConfig(uint16_t I2C_NACKPosition); +void I2C_SMBusAlertConfig(uint16_t I2C_SMBusAlert); +void I2C_TransmitPEC(FunctionalState NewState); +void I2C_PECPositionConfig(uint16_t I2C_PECPosition); +void I2C_CalculatePEC(FunctionalState NewState); + +uint8_t I2C_GetPEC(void); + +void I2C_ARPCmd(FunctionalState NewState); +void I2C_StretchClockCmd(FunctionalState NewState); +void I2C_FastModeDutyCycleConfig(uint16_t I2C_DutyCycle); + +/**************************************************************************************** + * I2C State Monitoring Functions + ****************************************************************************************/ +uint8_t I2C_CheckEvent(uint32_t I2C_EVENT); +uint32_t I2C_GetLastEvent(void); +FlagStatus I2C_GetFlagStatus(uint32_t I2C_FLAG); + +void I2C_ClearFlag(uint32_t I2C_FLAG); +ITStatus I2C_GetITStatus(uint32_t I2C_IT); +void I2C_ClearITPendingBit(uint32_t I2C_IT); + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_I2C_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_pwm.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_pwm.h new file mode 100644 index 0000000..75b018a --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_pwm.h @@ -0,0 +1,152 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_pwm.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#ifndef __CH58x_PWM_H__ +#define __CH58x_PWM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief channel of PWM define + */ +#define CH_PWM4 0x01 // PWM4 ͨ +#define CH_PWM5 0x02 // PWM5 ͨ +#define CH_PWM6 0x04 // PWM6 ͨ +#define CH_PWM7 0x08 // PWM7 ͨ +#define CH_PWM8 0x10 // PWM8 ͨ +#define CH_PWM9 0x20 // PWM9 ͨ +#define CH_PWM10 0x40 // PWM10 ͨ +#define CH_PWM11 0x80 // PWM11 ͨ + +/** + * @brief channel of PWM define + */ +typedef enum +{ + High_Level = 0, // Ĭϵ͵ƽߵƽЧ + Low_Level, // Ĭϸߵƽ͵ƽЧ +} PWMX_PolarTypeDef; + +/** + * @brief Configuration PWM4_11 Cycle size + */ +typedef enum +{ + PWMX_Cycle_256 = 0, // 256 PWMX + PWMX_Cycle_255, // 255 PWMX + PWMX_Cycle_128, // 128 PWMX + PWMX_Cycle_127, // 127 PWMX + PWMX_Cycle_64, // 64 PWMX + PWMX_Cycle_63, // 63 PWMX + PWMX_Cycle_32, // 32 PWMX + PWMX_Cycle_31, // 31 PWMX +} PWMX_CycleTypeDef; + +/** + * @brief PWM4-PWM11 ͨ׼ʱ + * + * @param d - ͨ׼ʱ = d*Tsys + */ +#define PWMX_CLKCfg(d) (R8_PWM_CLOCK_DIV = d) + +/** + * @brief PWM4-PWM11׼ʱ + * + * @param cyc - refer to PWMX_CycleTypeDef + */ +void PWMX_CycleCfg(PWMX_CycleTypeDef cyc); + +/** + * @brief PWM4 Ч + * + * @param d - Ч + */ +#define PWM4_ActDataWidth(d) (R8_PWM4_DATA = d) + +/** + * @brief PWM5 Ч + * + * @param d - Ч + */ +#define PWM5_ActDataWidth(d) (R8_PWM5_DATA = d) + +/** + * @brief PWM6 Ч + * + * @param d - Ч + */ +#define PWM6_ActDataWidth(d) (R8_PWM6_DATA = d) + +/** + * @brief PWM7 Ч + * + * @param d - Ч + */ +#define PWM7_ActDataWidth(d) (R8_PWM7_DATA = d) + +/** + * @brief PWM8 Ч + * + * @param d - Ч + */ +#define PWM8_ActDataWidth(d) (R8_PWM8_DATA = d) + +/** + * @brief PWM9 Ч + * + * @param d - Ч + */ +#define PWM9_ActDataWidth(d) (R8_PWM9_DATA = d) + +/** + * @brief PWM10 Ч + * + * @param d - Ч + */ +#define PWM10_ActDataWidth(d) (R8_PWM10_DATA = d) + +/** + * @brief PWM11 Ч + * + * @param d - Ч + */ +#define PWM11_ActDataWidth(d) (R8_PWM11_DATA = d) + +/** + * @brief PWM4-PWM11ͨ + * + * @param ch - select channel of pwm, refer to channel of PWM define + * @param da - effective pulse width + * @param pr - select wave polar, refer to PWMX_PolarTypeDef + * @param s - control pwmx function, ENABLE or DISABLE + */ +void PWMX_ACTOUT(uint8_t ch, uint8_t da, PWMX_PolarTypeDef pr, FunctionalState s); + +/** + * @brief PWM ģʽ + * + * @param ch - select group of PWM alternate output + * RB_PWM4_5_STAG_EN - PWM4 PWM5 ͨ + * RB_PWM6_7_STAG_EN - PWM6 PWM7 ͨ + * RB_PWM8_9_STAG_EN - PWM8 PWM9 ͨ + * RB_PWM10_11_STAG_EN - PWM10 PWM11 ͨ + * @param s - control pwmx function, ENABLE or DISABLE + */ +void PWMX_AlterOutCfg(uint8_t ch, FunctionalState s); + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_PWM_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_pwr.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_pwr.h new file mode 100644 index 0000000..abf4d08 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_pwr.h @@ -0,0 +1,167 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_pwr.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#ifndef __CH58x_PWR_H__ +#define __CH58x_PWR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Peripher CLK control bit define + */ +#define BIT_SLP_CLK_TMR0 (0x00000001) /*!< TMR0 peripher clk bit */ +#define BIT_SLP_CLK_TMR1 (0x00000002) /*!< TMR1 peripher clk bit */ +#define BIT_SLP_CLK_TMR2 (0x00000004) /*!< TMR2 peripher clk bit */ +#define BIT_SLP_CLK_TMR3 (0x00000008) /*!< TMR3 peripher clk bit */ +#define BIT_SLP_CLK_UART0 (0x00000010) /*!< UART0 peripher clk bit */ +#define BIT_SLP_CLK_UART1 (0x00000020) /*!< UART1 peripher clk bit */ +#define BIT_SLP_CLK_UART2 (0x00000040) /*!< UART2 peripher clk bit */ +#define BIT_SLP_CLK_UART3 (0x00000080) /*!< UART3 peripher clk bit */ +#define BIT_SLP_CLK_SPI0 (0x00000100) /*!< SPI0 peripher clk bit */ +//#define BIT_SLP_CLK_SPI1 (0x00000200) /*!< SPI1 peripher clk bit */ +#define BIT_SLP_CLK_PWMX (0x00000400) /*!< PWMX peripher clk bit */ +//#define BIT_SLP_CLK_LCD (0x00000800) /*!< LCD peripher clk bit */ +#define BIT_SLP_CLK_USB (0x00001000) /*!< USB peripher clk bit */ +//#define BIT_SLP_CLK_ETH (0x00002000) /*!< ETH peripher clk bit */ +//#define BIT_SLP_CLK_LED (0x00004000) /*!< LED peripher clk bit */ +#define BIT_SLP_CLK_BLE (0x00008000) /*!< BLE peripher clk bit */ + +#define BIT_SLP_CLK_RAMX (0x10000000) /*!< main SRAM RAM16K peripher clk bit */ +#define BIT_SLP_CLK_RAM2K (0x20000000) /*!< RAM2K peripher clk bit */ +#define BIT_SLP_CLK_ALL (0x3000FFFF) /*!< All peripher clk bit */ + +/** + * @brief unit of controllable power supply + */ +#define UNIT_SYS_LSE RB_CLK_XT32K_PON // ⲿ32K ʱ +#define UNIT_SYS_LSI RB_CLK_INT32K_PON // ڲ32K ʱ +#define UNIT_SYS_HSE RB_CLK_XT32M_PON // ⲿ32M ʱ +#define UNIT_SYS_PLL RB_CLK_PLL_PON // PLL ʱ + +/** + * @brief wakeup mode define + */ +typedef enum +{ + Short_Delay = 0, + Long_Delay, + +} WakeUP_ModeypeDef; + +/** + * @brief wakeup mode define + */ +typedef enum +{ + /* ȼʹø߾ȼأ210uA */ + HALevel_1V9 = 0, // 1.7-1.9 + HALevel_2V1, // 1.9-2.1 + HALevel_2V3, // 2.1-2.3 + HALevel_2V5, // 2.3-2.5 + + /* ȼʹõ͹ļأ1uA */ + LPLevel_1V8 = 0x80, + LPLevel_1V9, + LPLevel_2V0, + LPLevel_2V1, + LPLevel_2V2, + LPLevel_2V3, + LPLevel_2V4, + LPLevel_2V5, + +} VolM_LevelypeDef; + +/** + * @brief ڲDC/DCԴڽԼϵͳ + * + * @param s - ǷDCDCԴ + */ +void PWR_DCDCCfg(FunctionalState s); + +/** + * @brief ɿصԪģĵԴ + * + * @param s - Ƿ򿪵Դ + * @param unit - please refer to unit of controllable power supply + */ +void PWR_UnitModCfg(FunctionalState s, uint8_t unit); + +/** + * @brief ʱӿλ + * + * @param s - Ƿ򿪶Ӧʱ + * @param perph - please refer to Peripher CLK control bit define + */ +void PWR_PeriphClkCfg(FunctionalState s, uint16_t perph); + +/** + * @brief ˯߻Դ + * + * @param s - Ƿ򿪴˯߻ѹ + * @param perph - ҪõĻԴ + * RB_SLP_USB_WAKE - USB ΪԴ + * RB_SLP_RTC_WAKE - RTC ΪԴ + * RB_SLP_GPIO_WAKE - GPIO ΪԴ + * RB_SLP_BAT_WAKE - BAT ΪԴ + * @param mode - refer to WakeUP_ModeypeDef + */ +void PWR_PeriphWakeUpCfg(FunctionalState s, uint8_t perph, WakeUP_ModeypeDef mode); + +/** + * @brief Դ + * + * @param s - Ƿ򿪴˹ + * @param vl - refer to VolM_LevelypeDef + */ +void PowerMonitor(FunctionalState s, VolM_LevelypeDef vl); + +/** + * @brief ͹-Idleģʽ + */ +void LowPower_Idle(void); + +/** + * @brief ͹-Haltģʽ˵͹еHSI/5ʱУѺҪûԼѡϵͳʱԴ + */ +void LowPower_Halt(void); + +/** + * @brief ͹-Sleepģʽ˵͹еHSI/5ʱУѺҪûԼѡϵͳʱԴ + * @note עô˺DCDCǿƹرգѺֶٴδ + * + * @param rm - ģѡ + * RB_PWR_RAM2K - 2K retention SRAM + * RB_PWR_RAM16K - 16K main SRAM + * RB_PWR_EXTEND - USB BLE Ԫ򹩵 + * RB_PWR_XROM - FlashROM + * NULL - ϵԪϵ + */ +void LowPower_Sleep(uint8_t rm); + +/** + * @brief ͹-Shutdownģʽ˵͹еHSI/5ʱУѺҪûԼѡϵͳʱԴ + * @note עô˺DCDCǿƹرգѺֶٴδ + * + * @param rm - ģѡ + * RB_PWR_RAM2K - 2K retention SRAM + * RB_PWR_RAM16K - 16K main SRAM + * NULL - ϵԪϵ + */ +void LowPower_Shutdown(uint8_t rm); + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_PWR_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_spi.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_spi.h new file mode 100644 index 0000000..6d7b176 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_spi.h @@ -0,0 +1,209 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_SPI.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#ifndef __CH58x_SPI_H__ +#define __CH58x_SPI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief SPI0 interrupt bit define + */ +#define SPI0_IT_FST_BYTE RB_SPI_IE_FST_BYTE // ӻģʽֽģʽ£յֽж +#define SPI0_IT_FIFO_OV RB_SPI_IE_FIFO_OV // FIFO +#define SPI0_IT_DMA_END RB_SPI_IE_DMA_END // DMA +#define SPI0_IT_FIFO_HF RB_SPI_IE_FIFO_HF // FIFO ʹù +#define SPI0_IT_BYTE_END RB_SPI_IE_BYTE_END // ֽڴ +#define SPI0_IT_CNT_END RB_SPI_IE_CNT_END // ȫֽڴ + +/** + * @brief Configuration data mode + */ +typedef enum +{ + Mode0_LowBitINFront = 0, // ģʽ0λǰ + Mode0_HighBitINFront, // ģʽ0λǰ + Mode3_LowBitINFront, // ģʽ3λǰ + Mode3_HighBitINFront, // ģʽ3λǰ +} ModeBitOrderTypeDef; + +/** + * @brief Configuration SPI0 slave mode + */ +typedef enum +{ + Mode_DataStream = 0, // ģʽ + Mose_FirstCmd, // ֽģʽ +} Slave_ModeTypeDef; + +/** + * @brief ģʽĬϳʼģʽ0+3ȫ˫+8MHz + */ +void SPI0_MasterDefInit(void); + +/** + * @brief SPI0 ׼ʱã= d*Tsys + * + * @param c - ʱӷƵϵ + */ +void SPI0_CLKCfg(uint8_t c); + +/** + * @brief ģʽ + * + * @param m - ģʽ refer to ModeBitOrderTypeDef + */ +void SPI0_DataMode(ModeBitOrderTypeDef m); + +/** + * @brief ͵ֽ (buffer) + * + * @param d - ֽ + */ +void SPI0_MasterSendByte(uint8_t d); + +/** + * @brief յֽ (buffer) + * + * @param none + */ +uint8_t SPI0_MasterRecvByte(void); + +/** + * @brief ʹFIFOͶֽ + * + * @param pbuf - ͵׵ַ + * @param len - ͵ݳȣ4095 + */ +void SPI0_MasterTrans(uint8_t *pbuf, uint16_t len); + +/** + * @brief ʹFIFOնֽ + * + * @param pbuf - յ׵ַ + * @param len - յݳȣ4095 + */ +void SPI0_MasterRecv(uint8_t *pbuf, uint16_t len); + +/** + * @brief DMAʽ + * + * @param pbuf - ʼַ,Ҫֽڶ + * @param len - ݳ + */ +void SPI0_MasterDMATrans(uint8_t *pbuf, uint16_t len); + +/** + * @brief DMAʽ + * + * @param pbuf - ݴʼַ,Ҫֽڶ + * @param len - ݳ + */ +void SPI0_MasterDMARecv(uint8_t *pbuf, uint16_t len); + +void SPI1_MasterDefInit(void); /* ģʽĬϳʼģʽ0+3ȫ˫+8MHz */ +void SPI1_CLKCfg(UINT8 c); /* SPI1 ׼ʱã= d*Tsys */ +void SPI1_DataMode(ModeBitOrderTypeDef m); /* ģʽ */ + +void SPI1_MasterSendByte(UINT8 d); /* ͵ֽ (buffer) */ +UINT8 SPI1_MasterRecvByte(void); /* յֽ (buffer) */ + +void SPI1_MasterTrans(UINT8 *pbuf, UINT16 len); /* ʹFIFOͶֽ */ +void SPI1_MasterRecv(UINT8 *pbuf, UINT16 len); /* ʹFIFOնֽ */ + +/** + * @brief 豸ģʽĬϳʼMISOGPIOӦΪģʽ + */ +void SPI0_SlaveInit(void); + +/** + * @brief ֽ + * + * @param d - ֽ + */ +#define SetFirstData(d) (R8_SPI0_SLAVE_PRE = d) + +/** + * @brief ӻģʽһֽ + * + * @param d - + */ +void SPI0_SlaveSendByte(uint8_t d); + +/** + * @brief ӻģʽһֽ + * + * @return յ + */ +uint8_t SPI0_SlaveRecvByte(void); + +/** + * @brief ӻģʽͶֽ + * + * @param pbuf - ͵׵ַ + * @param len - ͵ݳȣ4095 + */ +void SPI0_SlaveTrans(uint8_t *pbuf, uint16_t len); + +/** + * @brief ӻģʽնֽ + * + * @param pbuf - ݴʼַ + * @param len - ݳ + */ +void SPI0_SlaveRecv(uint8_t *pbuf, uint16_t len); + +/** + * @brief DMAʽ + * + * @param pbuf - ʼַ,Ҫֽڶ + * @param len - ݳ + */ +void SPI0_SlaveDMATrans(uint8_t *pbuf, uint16_t len); + +/** + * @brief DMAʽ + * + * @param pbuf - ݴʼַ,Ҫֽڶ + * @param len - ݳ + */ +void SPI0_SlaveDMARecv(uint8_t *pbuf, uint16_t len); + +/** + * @brief SPI0ж + * + * @param s - ʹ/ر + * @param f - refer to SPI0 interrupt bit define + */ +#define SPI0_ITCfg(s, f) ((s) ? (R8_SPI0_INTER_EN |= f) : (R8_SPI0_INTER_EN &= ~f)) + +/** + * @brief ȡжϱ־״̬0-δλ(!0)- + * + * @param f - refer to SPI0 interrupt bit define + */ +#define SPI0_GetITFlag(f) (R8_SPI0_INT_FLAG & f) + +/** + * @brief ǰжϱ־ + * + * @param f - refer to SPI0 interrupt bit define + */ +#define SPI0_ClearITFlag(f) (R8_SPI0_INT_FLAG = f) + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_SPI_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_sys.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_sys.h new file mode 100644 index 0000000..b98a7f7 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_sys.h @@ -0,0 +1,194 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_SYS.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#ifndef __CH58x_SYS_H__ +#define __CH58x_SYS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief rtc interrupt event define + */ +typedef enum +{ + RST_STATUS_SW = 0, // λ + RST_STATUS_RPOR, // ϵ縴λ + RST_STATUS_WTR, // Źʱλ + RST_STATUS_MR, // ⲿֶλ + RST_STATUS_LRM0, // Ѹλ-λ + RST_STATUS_GPWSM, // µģʽѸλ + RST_STATUS_LRM1, // Ѹλ-Ź + RST_STATUS_LRM2, // Ѹλ-ֶλ + +} SYS_ResetStaTypeDef; + +/** + * @brief rtc interrupt event define + */ +typedef enum +{ + INFO_ROM_READ = 0, // FlashROM Ƿɶ + INFO_RESET_EN = 2, // RST#ⲿֶλ빦Ƿ + INFO_BOOT_EN, // ϵͳ BootLoader Ƿ + INFO_DEBUG_EN, // ϵͳԽӿǷ + INFO_LOADER, // ǰϵͳǷBootloader + STA_SAFEACC_ACT, // ǰϵͳǷڰȫ״̬RWA򲻿ɷ + +} SYS_InfoStaTypeDef; + +/** + * @brief ȡоƬID࣬һΪ̶ֵ + */ +#define SYS_GetChipID() R8_CHIP_ID + +/** + * @brief ȡȫIDһΪ̶ֵ + */ +#define SYS_GetAccessID() R8_SAFE_ACCESS_ID + +/** + * @brief ϵͳʱ + * + * @param sc - ϵͳʱԴѡ refer to SYS_CLKTypeDef + */ +void SetSysClock(SYS_CLKTypeDef sc); + +/** + * @brief ȡǰϵͳʱ + * + * @return Hz + */ +uint32_t GetSysClock(void); + +/** + * @brief ȡǰϵͳϢ״̬ + * + * @param i - refer to SYS_InfoStaTypeDef + * + * @return Ƿ + */ +uint8_t SYS_GetInfoSta(SYS_InfoStaTypeDef i); + +/** + * @brief ȡϵͳϴθλ״̬ + * + * @return refer to SYS_ResetStaTypeDef + */ +#define SYS_GetLastResetSta() (R8_RESET_STATUS & RB_RESET_FLAG) + +/** + * @brief ִϵͳλ + */ +void SYS_ResetExecute(void); + +/** + * @brief øλĴֵֶλ λ ŹλͨѸλӰ + * + * @param i - refer to SYS_InfoStaTypeDef + */ +#define SYS_ResetKeepBuf(d) (R8_GLOB_RESET_KEEP = d) + +/** + * @brief رжϣǰжֵ + * + * @param pirqv - ǰжֵ + */ +void SYS_DisableAllIrq(uint32_t *pirqv); + +/** + * @brief ָ֮ǰرյжֵ + * + * @param irq_status - ǰжֵ + */ +void SYS_RecoverIrq(uint32_t irq_status); + +/** + * @brief ȡǰϵͳ(SYSTICK)ֵ + * + * @return ǰֵ + */ +uint32_t SYS_GetSysTickCnt(void); + +/** + * @brief ؿŹֵ + * + * @param c - Źֵ + */ +#define WWDG_SetCounter(c) (R8_WDOG_COUNT = c) + +/** + * @brief Źʱжʹ + * + * @param s - Ƿж + */ +void WWDG_ITCfg(FunctionalState s); + +/** + * @brief Źʱλ + * + * @param s - Ƿλ + */ +void WWDG_ResetCfg(FunctionalState s); + +/** + * @brief ȡǰŹʱ־ + * + * @return Źʱ־ + */ +#define WWDG_GetFlowFlag() (R8_RST_WDOG_CTRL & RB_WDOG_INT_FLAG) + +/** + * @brief Źжϱ־¼ؼֵҲ + */ +void WWDG_ClearFlag(void); + +/** + * @brief uS ʱ + * + * @param t - ʱ + */ +void mDelayuS(uint16_t t); + +/** + * @brief mS ʱ + * + * @param t - ʱ + */ +void mDelaymS(uint16_t t); + +/** + * @brief Enter safe access mode. + * + * @NOTE: After enter safe access mode, about 16 system frequency cycles + * are in safe mode, and one or more secure registers can be rewritten + * within the valid period. The safe mode will be automatically + * terminated after the above validity period is exceeded. + */ + __attribute__((always_inline)) static inline void sys_safe_access_enable(void) +{ + R8_SAFE_ACCESS_SIG = SAFE_ACCESS_SIG1; + R8_SAFE_ACCESS_SIG = SAFE_ACCESS_SIG2; + SAFEOPERATE; +} + +__attribute__((always_inline)) static inline void sys_safe_access_disable(void) +{ + R8_SAFE_ACCESS_SIG = 0; +} + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_SYS_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_timer.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_timer.h new file mode 100644 index 0000000..0da5fa3 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_timer.h @@ -0,0 +1,555 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_timer.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#ifndef __CH58x_TIMER_H__ +#define __CH58x_TIMER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define DataBit_25 (1 << 25) + +/** + * @brief TMR0 interrupt bit define + */ + +#define TMR0_3_IT_CYC_END 0x01 // ڽ־׽-ʱʱ-ڽPWM-ڽ +#define TMR0_3_IT_DATA_ACT 0x02 // Ч־׽-ݣPWM-Чƽ +#define TMR0_3_IT_FIFO_HF 0x04 // FIFO ʹù룺׽- FIFO>=4 PWM- FIFO<4 +#define TMR1_2_IT_DMA_END 0x08 // DMA ֧TMR1TMR2 +#define TMR0_3_IT_FIFO_OV 0x10 // FIFO ׽- FIFO PWM- FIFO + +/** + * @brief Configuration PWM effective level repeat times + */ +typedef enum +{ + PWM_Times_1 = 0, // PWM Чظ1 + PWM_Times_4, // PWM Чظ4 + PWM_Times_8, // PWM Чظ8 + PWM_Times_16, // PWM Чظ16 +} PWM_RepeatTsTypeDef; + +/** + * @brief Configuration Cap mode + */ +typedef enum +{ + CAP_NULL = 0, // ׽ & + Edge_To_Edge, // ֮ & + FallEdge_To_FallEdge, // ½ص½ & ½ + RiseEdge_To_RiseEdge, // ص & +} CapModeTypeDef; + +/** + * @brief Configuration DMA mode + */ +typedef enum +{ + Mode_Single = 0, // ģʽ + Mode_LOOP, // ѭģʽ +} DMAModeTypeDef; + +/** + * @brief ʱܳʼ + * + * @param t - ʱʱ䣬ڵǰϵͳʱTsys, ʱ 67108864 + */ +void TMR0_TimerInit(uint32_t t); + +/** + * @brief ȡǰʱֵ67108864 + * + * @return ǰʱֵ + */ +#define TMR0_GetCurrentTimer() R32_TMR0_COUNT + +/** + * @brief ؼܳʼ + * + * @param cap - ɼ + */ +void TMR0_EXTSingleCounterInit(CapModeTypeDef cap); + +/** + * @brief üͳС67108862 + * + * @param cyc - ͳС + */ +#define TMR0_CountOverflowCfg(cyc) (R32_TMR0_CNT_END = (cyc + 2)) + +/** + * @brief ȡǰֵ67108862 + * + * @return ǰֵ + */ +#define TMR0_GetCurrentCount() R32_TMR0_COUNT + +/** + * @brief PWM0 ͨ, 67108864 + * + * @param cyc - + */ +#define TMR0_PWMCycleCfg(cyc) (R32_TMR0_CNT_END = cyc) + +/** + * @brief PWM ʼ + * + * @param pr - select wave polar, refer to PWMX_PolarTypeDef + * @param ts - set pwm repeat times, refer to PWM_RepeatTsTypeDef + */ +void TMR0_PWMInit(PWMX_PolarTypeDef pr, PWM_RepeatTsTypeDef ts); + +/** + * @brief PWM0 Ч, 67108864 + * + * @param d - Ч + */ +#define TMR0_PWMActDataWidth(d) (R32_TMR0_FIFO = d) + +/** + * @brief CAP0 ׽ƽʱ, 33554432 + * + * @param cyc - ׽ƽʱ + */ +#define TMR0_CAPTimeoutCfg(cyc) (R32_TMR0_CNT_END = cyc) + +/** + * @brief ⲿźŲ׽ܳʼ + * + * @param cap - select capture mode, refer to CapModeTypeDef + */ +void TMR0_CapInit(CapModeTypeDef cap); + +/** + * @brief ȡ + * + * @return + */ +#define TMR0_CAPGetData() R32_TMR0_FIFO + +/** + * @brief ȡǰѲݸ + * + * @return ǰѲݸ + */ +#define TMR0_CAPDataCounter() R8_TMR0_FIFO_COUNT + +/** + * @brief ر TMR0 + */ +#define TMR0_Disable() (R8_TMR0_CTRL_MOD &= ~RB_TMR_COUNT_EN) + +/** + * @brief TMR0 + */ +#define TMR0_Enable() (R8_TMR0_CTRL_MOD |= RB_TMR_COUNT_EN) + +/** + * @brief ж + * + * @param s - ʹ/ر + * @param f - refer to TMR interrupt bit define + */ +#define TMR0_ITCfg(s, f) ((s) ? (R8_TMR0_INTER_EN |= f) : (R8_TMR0_INTER_EN &= ~f)) + +/** + * @brief жϱ־ + * + * @param f - refer to TMR interrupt bit define + */ +#define TMR0_ClearITFlag(f) (R8_TMR0_INT_FLAG = f) + +/** + * @brief ѯжϱ־״̬ + * + * @param f - refer to TMR interrupt bit define + */ +#define TMR0_GetITFlag(f) (R8_TMR0_INT_FLAG & f) + +/** + * @brief ʱܳʼ + * + * @param t - ʱʱ䣬ڵǰϵͳʱTsys, ʱ 67108864 + */ +void TMR1_TimerInit(uint32_t t); + +/** + * @brief ȡǰʱֵ67108864 + * + * @return ǰʱֵ + */ +#define TMR1_GetCurrentTimer() R32_TMR1_COUNT + +/** + * @brief ؼܳʼ + * + * @param cap - ɼ + */ +void TMR1_EXTSingleCounterInit(CapModeTypeDef cap); + +/** + * @brief üͳС67108862 + * + * @param cyc - ͳС + */ +#define TMR1_CountOverflowCfg(cyc) (R32_TMR1_CNT_END = (cyc + 2)) + +/** + * @brief ȡǰֵ67108862 + * + * @return ǰֵ + */ +#define TMR1_GetCurrentCount() R32_TMR1_COUNT + +/** + * @brief PWM1 ͨ, 67108864 + * + * @param cyc - + */ +#define TMR1_PWMCycleCfg(cyc) (R32_TMR1_CNT_END = cyc) + +/** + * @brief PWM ʼ + * + * @param pr - select wave polar, refer to PWMX_PolarTypeDef + * @param ts - set pwm repeat times, refer to PWM_RepeatTsTypeDef + */ +void TMR1_PWMInit(PWMX_PolarTypeDef pr, PWM_RepeatTsTypeDef ts); + +/** + * @brief PWM1 Ч, 67108864 + * + * @param d - Ч + */ +#define TMR1_PWMActDataWidth(d) (R32_TMR1_FIFO = d) + +/** + * @brief CAP1 ׽ƽʱ, 33554432 + * + * @param cyc - ׽ƽʱ + */ +#define TMR1_CAPTimeoutCfg(cyc) (R32_TMR1_CNT_END = cyc) + +/** + * @brief ⲿźŲ׽ܳʼ + * + * @param cap - select capture mode, refer to CapModeTypeDef + */ +void TMR1_CapInit(CapModeTypeDef cap); + +/** + * @brief ȡ + * + * @return + */ +#define TMR1_CAPGetData() R32_TMR1_FIFO + +/** + * @brief ȡǰѲݸ + * + * @return ǰѲݸ + */ +#define TMR1_CAPDataCounter() R8_TMR1_FIFO_COUNT + +/** + * @brief DMA + * + * @param s - ǷDMA + * @param startAddr - DMA ʼַ + * @param endAddr - DMA ַ + * @param m - DMAģʽ + */ +void TMR1_DMACfg(uint8_t s, uint16_t startAddr, uint16_t endAddr, DMAModeTypeDef m); + +/** + * @brief ر TMR1 + */ +#define TMR1_Disable() (R8_TMR1_CTRL_MOD &= ~RB_TMR_COUNT_EN) + +/** + * @brief TMR1 + */ +#define TMR1_Enable() (R8_TMR1_CTRL_MOD |= RB_TMR_COUNT_EN) + +/** + * @brief ж + * + * @param s - ʹ/ر + * @param f - refer to TMR interrupt bit define + */ +#define TMR1_ITCfg(s, f) ((s) ? (R8_TMR1_INTER_EN |= f) : (R8_TMR1_INTER_EN &= ~f)) + +/** + * @brief жϱ־ + * + * @param f - refer to TMR interrupt bit define + */ +#define TMR1_ClearITFlag(f) (R8_TMR1_INT_FLAG = f) + +/** + * @brief ѯжϱ־״̬ + * + * @param f - refer to TMR interrupt bit define + */ +#define TMR1_GetITFlag(f) (R8_TMR1_INT_FLAG & f) + +/** + * @brief ʱܳʼ + * + * @param t - ʱʱ䣬ڵǰϵͳʱTsys, ʱ 67108864 + */ +void TMR2_TimerInit(uint32_t t); + +/** + * @brief ȡǰʱֵ67108864 + * + * @return ǰʱֵ + */ +#define TMR2_GetCurrentTimer() R32_TMR2_COUNT + +/** + * @brief ؼܳʼ + * + * @param cap - ɼ + */ +void TMR2_EXTSingleCounterInit(CapModeTypeDef cap); + +/** + * @brief üͳС67108862 + * + * @param cyc - ͳС + */ +#define TMR2_CountOverflowCfg(cyc) (R32_TMR2_CNT_END = (cyc + 2)) + +/** + * @brief ȡǰֵ67108862 + * + * @return ǰֵ + */ +#define TMR2_GetCurrentCount() R32_TMR2_COUNT + +/** + * @brief PWM2 ͨ, 67108864 + * + * @param cyc - + */ +#define TMR2_PWMCycleCfg(cyc) (R32_TMR2_CNT_END = cyc) + +/** + * @brief PWM ʼ + * + * @param pr - select wave polar, refer to PWMX_PolarTypeDef + * @param ts - set pwm repeat times, refer to PWM_RepeatTsTypeDef + */ +void TMR2_PWMInit(PWMX_PolarTypeDef pr, PWM_RepeatTsTypeDef ts); + +/** + * @brief PWM2 Ч, 67108864 + * + * @param d - Ч + */ +#define TMR2_PWMActDataWidth(d) (R32_TMR2_FIFO = d) + +/** + * @brief CAP2 ׽ƽʱ, 33554432 + * + * @param cyc - ׽ƽʱ + */ +#define TMR2_CAPTimeoutCfg(cyc) (R32_TMR2_CNT_END = cyc) + +/** + * @brief ⲿźŲ׽ܳʼ + * + * @param cap - select capture mode, refer to CapModeTypeDef + */ +void TMR2_CapInit(CapModeTypeDef cap); + +/** + * @brief ȡ + * + * @return + */ +#define TMR2_CAPGetData() R32_TMR2_FIFO + +/** + * @brief ȡǰѲݸ + * + * @return ǰѲݸ + */ +#define TMR2_CAPDataCounter() R8_TMR2_FIFO_COUNT + +/** + * @brief DMA + * + * @param s - ǷDMA + * @param startAddr - DMA ʼַ + * @param endAddr - DMA ַ + * @param m - DMAģʽ + */ +void TMR2_DMACfg(uint8_t s, uint16_t startAddr, uint16_t endAddr, DMAModeTypeDef m); + +/** + * @brief ر TMR2 + */ +#define TMR2_Disable() (R8_TMR2_CTRL_MOD &= ~RB_TMR_COUNT_EN) + +/** + * @brief TMR2 + */ +#define TMR2_Enable() (R8_TMR2_CTRL_MOD |= RB_TMR_COUNT_EN) + +/** + * @brief ж + * + * @param s - ʹ/ر + * @param f - refer to TMR interrupt bit define + */ +#define TMR2_ITCfg(s, f) ((s) ? (R8_TMR2_INTER_EN |= f) : (R8_TMR2_INTER_EN &= ~f)) + +/** + * @brief жϱ־ + * + * @param f - refer to TMR interrupt bit define + */ +#define TMR2_ClearITFlag(f) (R8_TMR2_INT_FLAG = f) + +/** + * @brief ѯжϱ־״̬ + * + * @param f - refer to TMR interrupt bit define + */ +#define TMR2_GetITFlag(f) (R8_TMR2_INT_FLAG & f) + +/** + * @brief ʱܳʼ + * + * @param t - ʱʱ䣬ڵǰϵͳʱTsys, ʱ 67108864 + */ +void TMR3_TimerInit(uint32_t t); + +/** + * @brief ȡǰʱֵ67108864 + * + * @return ǰʱֵ + */ +#define TMR3_GetCurrentTimer() R32_TMR3_COUNT + +/** + * @brief ؼܳʼ + * + * @param cap - ɼ + */ +void TMR3_EXTSingleCounterInit(CapModeTypeDef cap); + +/** + * @brief üͳС67108862 + * + * @param cyc - ͳС + */ +#define TMR3_CountOverflowCfg(cyc) (R32_TMR3_CNT_END = (cyc + 2)) + +/** + * @brief ȡǰֵ67108862 + * + * @return ǰֵ + */ +#define TMR3_GetCurrentCount() R32_TMR3_COUNT + +/** + * @brief PWM3 ͨ, 67108864 + * + * @param cyc - + */ +#define TMR3_PWMCycleCfg(cyc) (R32_TMR3_CNT_END = cyc) + +/** + * @brief PWM ʼ + * + * @param pr - select wave polar, refer to PWMX_PolarTypeDef + * @param ts - set pwm repeat times, refer to PWM_RepeatTsTypeDef + */ +void TMR3_PWMInit(PWMX_PolarTypeDef pr, PWM_RepeatTsTypeDef ts); + +/** + * @brief PWM3 Ч, 67108864 + * + * @param d - Ч + */ +#define TMR3_PWMActDataWidth(d) (R32_TMR3_FIFO = d) + +/** + * @brief CAP3 ׽ƽʱ, 33554432 + * + * @param cyc - ׽ƽʱ + */ +#define TMR3_CAPTimeoutCfg(cyc) (R32_TMR3_CNT_END = cyc) + +/** + * @brief ⲿźŲ׽ܳʼ + * + * @param cap - select capture mode, refer to CapModeTypeDef + */ +void TMR3_CapInit(CapModeTypeDef cap); + +/** + * @brief ȡ + * + * @return + */ +#define TMR3_CAPGetData() R32_TMR3_FIFO + +/** + * @brief ȡǰѲݸ + * + * @return ǰѲݸ + */ +#define TMR3_CAPDataCounter() R8_TMR3_FIFO_COUNT + +/** + * @brief ر TMR3 + */ +#define TMR3_Disable() (R8_TMR3_CTRL_MOD &= ~RB_TMR_COUNT_EN) + +/** + * @brief TMR3 + */ +#define TMR3_Enable() (R8_TMR3_CTRL_MOD |= RB_TMR_COUNT_EN) + +/** + * @brief ж + * + * @param s - ʹ/ر + * @param f - refer to TMR interrupt bit define + */ +#define TMR3_ITCfg(s, f) ((s) ? (R8_TMR3_INTER_EN |= f) : (R8_TMR3_INTER_EN &= ~f)) + +/** + * @brief жϱ־ + * + * @param f - refer to TMR interrupt bit define + */ +#define TMR3_ClearITFlag(f) (R8_TMR3_INT_FLAG = f) + +/** + * @brief ѯжϱ־״̬ + * + * @param f - refer to TMR interrupt bit define + */ +#define TMR3_GetITFlag(f) (R8_TMR3_INT_FLAG & f) + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_TIMER_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_uart.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_uart.h new file mode 100644 index 0000000..75f8244 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_uart.h @@ -0,0 +1,412 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_uart.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#ifndef __CH58x_UART_H__ +#define __CH58x_UART_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LINE error and status define + */ +#define STA_ERR_BREAK RB_LSR_BREAK_ERR // ݼ +#define STA_ERR_FRAME RB_LSR_FRAME_ERR // ֡ +#define STA_ERR_PAR RB_LSR_PAR_ERR // żУλ +#define STA_ERR_FIFOOV RB_LSR_OVER_ERR // + +#define STA_TXFIFO_EMP RB_LSR_TX_FIFO_EMP // ǰFIFOգԼ䷢ +#define STA_TXALL_EMP RB_LSR_TX_ALL_EMP // ǰзݶ +#define STA_RECV_DATA RB_LSR_DATA_RDY // ǰнյ + +/** + * @brief Configuration UART TrigByte num + */ +typedef enum +{ + UART_1BYTE_TRIG = 0, // 1ֽڴ + UART_2BYTE_TRIG, // 2ֽڴ + UART_4BYTE_TRIG, // 4ֽڴ + UART_7BYTE_TRIG, // 7ֽڴ + +} UARTByteTRIGTypeDef; + +/** + * @brief Ĭϳʼ + */ +void UART0_DefInit(void); + +/** + * @brief ڲ + * + * @param baudrate - + */ +void UART0_BaudRateCfg(uint32_t baudrate); + +/** + * @brief ֽڴж + * + * @param b - ֽ refer to UARTByteTRIGTypeDef + */ +void UART0_ByteTrigCfg(UARTByteTRIGTypeDef b); + +/** + * @brief ж + * + * @param s - жϿ״̬ǷʹӦж + * @param i - ж + * RB_IER_MODEM_CHG - ƽ״̬仯жʹλ UART0 ֧֣ + * RB_IER_LINE_STAT - ·״̬ж + * RB_IER_THR_EMPTY - ͱּĴж + * RB_IER_RECV_RDY - ж + */ +void UART0_INTCfg(FunctionalState s, uint8_t i); + +/** + * @brief λ + */ +void UART0_Reset(void); + +/** + * @brief ǰFIFO + */ +#define UART0_CLR_RXFIFO() (R8_UART0_FCR |= RB_FCR_RX_FIFO_CLR) + +/** + * @brief ǰFIFO + */ +#define UART0_CLR_TXFIFO() (R8_UART0_FCR |= RB_FCR_TX_FIFO_CLR) + +/** + * @brief ȡǰжϱ־ + * + * @return ǰжϱ־ + */ +#define UART0_GetITFlag() (R8_UART0_IIR & RB_IIR_INT_MASK) + +/** + * @brief ȡǰͨѶ״̬ + * + * @return refer to LINE error and status define + */ +#define UART0_GetLinSTA() (R8_UART0_LSR) + +/** + * @brief ڵֽڷ + * + * @param b ͵ֽ + */ +#define UART0_SendByte(b) (R8_UART0_THR = b) + +/** + * @brief ڶֽڷ + * + * @param buf - ͵׵ַ + * @param l - ͵ݳ + */ +void UART0_SendString(uint8_t *buf, uint16_t l); + +/** + * @brief ڶȡֽ + * + * @return ȡĵֽ + */ +#define UART0_RecvByte() (R8_UART0_RBR) + +/** + * @brief ڶȡֽ + * + * @param buf - ȡݴŻ׵ַ + * + * @return ȡݳ + */ +uint16_t UART0_RecvString(uint8_t *buf); + +/** + * @brief Ĭϳʼ + */ +void UART1_DefInit(void); + +/** + * @brief ڲ + * + * @param baudrate - + */ +void UART1_BaudRateCfg(uint32_t baudrate); + +/** + * @brief ֽڴж + * + * @param b - ֽ refer to UARTByteTRIGTypeDef + */ +void UART1_ByteTrigCfg(UARTByteTRIGTypeDef b); + +/** + * @brief ж + * + * @param s - жϿ״̬ǷʹӦж + * @param i - ж + * RB_IER_MODEM_CHG - ƽ״̬仯жʹλ UART0 ֧֣ + * RB_IER_LINE_STAT - ·״̬ж + * RB_IER_THR_EMPTY - ͱּĴж + * RB_IER_RECV_RDY - ж + */ +void UART1_INTCfg(FunctionalState s, uint8_t i); + +/** + * @brief λ + */ +void UART1_Reset(void); + +/** + * @brief ǰFIFO + */ +#define UART1_CLR_RXFIFO() (R8_UART1_FCR |= RB_FCR_RX_FIFO_CLR) + +/** + * @brief ǰFIFO + */ +#define UART1_CLR_TXFIFO() (R8_UART1_FCR |= RB_FCR_TX_FIFO_CLR) + +/** + * @brief ȡǰжϱ־ + * + * @return ǰжϱ־ + */ +#define UART1_GetITFlag() (R8_UART1_IIR & RB_IIR_INT_MASK) + +/** + * @brief ȡǰͨѶ״̬ + * + * @return refer to LINE error and status define + */ +#define UART1_GetLinSTA() (R8_UART1_LSR) + +/** + * @brief ڵֽڷ + * + * @param b ͵ֽ + */ +#define UART1_SendByte(b) (R8_UART1_THR = b) + +/** + * @brief ڶֽڷ + * + * @param buf - ͵׵ַ + * @param l - ͵ݳ + */ +void UART1_SendString(uint8_t *buf, uint16_t l); + +/** + * @brief ڶȡֽ + * + * @return ȡĵֽ + */ +#define UART1_RecvByte() (R8_UART1_RBR) + +/** + * @brief ڶȡֽ + * + * @param buf - ȡݴŻ׵ַ + * + * @return ȡݳ + */ +uint16_t UART1_RecvString(uint8_t *buf); + +/** + * @brief Ĭϳʼ + */ +void UART2_DefInit(void); + +/** + * @brief ڲ + * + * @param baudrate - + */ +void UART2_BaudRateCfg(uint32_t baudrate); + +/** + * @brief ֽڴж + * + * @param b - ֽ refer to UARTByteTRIGTypeDef + */ +void UART2_ByteTrigCfg(UARTByteTRIGTypeDef b); + +/** + * @brief ж + * + * @param s - жϿ״̬ǷʹӦж + * @param i - ж + * RB_IER_MODEM_CHG - ƽ״̬仯жʹλ UART0 ֧֣ + * RB_IER_LINE_STAT - ·״̬ж + * RB_IER_THR_EMPTY - ͱּĴж + * RB_IER_RECV_RDY - ж + */ +void UART2_INTCfg(FunctionalState s, uint8_t i); + +/** + * @brief λ + */ +void UART2_Reset(void); + +/** + * @brief ǰFIFO + */ +#define UART2_CLR_RXFIFO() (R8_UART2_FCR |= RB_FCR_RX_FIFO_CLR) + +/** + * @brief ǰFIFO + */ +#define UART2_CLR_TXFIFO() (R8_UART2_FCR |= RB_FCR_TX_FIFO_CLR) + +/** + * @brief ȡǰжϱ־ + * + * @return ǰжϱ־ + */ +#define UART2_GetITFlag() (R8_UART2_IIR & RB_IIR_INT_MASK) + +/** + * @brief ȡǰͨѶ״̬ + * + * @return refer to LINE error and status define + */ +#define UART2_GetLinSTA() (R8_UART2_LSR) + +/** + * @brief ڵֽڷ + * + * @param b ͵ֽ + */ +#define UART2_SendByte(b) (R8_UART2_THR = b) + +/** + * @brief ڶֽڷ + * + * @param buf - ͵׵ַ + * @param l - ͵ݳ + */ +void UART2_SendString(uint8_t *buf, uint16_t l); + +/** + * @brief ڶȡֽ + * + * @return ȡĵֽ + */ +#define UART2_RecvByte() (R8_UART2_RBR) + +/** + * @brief ڶȡֽ + * + * @param buf - ȡݴŻ׵ַ + * + * @return ȡݳ + */ +uint16_t UART2_RecvString(uint8_t *buf); + +/** + * @brief Ĭϳʼ + */ +void UART3_DefInit(void); + +/** + * @brief ڲ + * + * @param baudrate - + */ +void UART3_BaudRateCfg(uint32_t baudrate); + +/** + * @brief ֽڴж + * + * @param b - ֽ refer to UARTByteTRIGTypeDef + */ +void UART3_ByteTrigCfg(UARTByteTRIGTypeDef b); + +/** + * @brief ж + * + * @param s - жϿ״̬ǷʹӦж + * @param i - ж + * RB_IER_MODEM_CHG - ƽ״̬仯жʹλ UART0 ֧֣ + * RB_IER_LINE_STAT - ·״̬ж + * RB_IER_THR_EMPTY - ͱּĴж + * RB_IER_RECV_RDY - ж + */ +void UART3_INTCfg(FunctionalState s, uint8_t i); + +/** + * @brief λ + */ +void UART3_Reset(void); + +/** + * @brief ǰFIFO + */ +#define UART3_CLR_RXFIFO() (R8_UART3_FCR |= RB_FCR_RX_FIFO_CLR) + +/** + * @brief ǰFIFO + */ +#define UART3_CLR_TXFIFO() (R8_UART3_FCR |= RB_FCR_TX_FIFO_CLR) + +/** + * @brief ȡǰжϱ־ + * + * @return ǰжϱ־ + */ +#define UART3_GetITFlag() (R8_UART3_IIR & RB_IIR_INT_MASK) + +/** + * @brief ȡǰͨѶ״̬ + * + * @return refer to LINE error and status define + */ +#define UART3_GetLinSTA() (R8_UART3_LSR) + +/** + * @brief ڵֽڷ + * + * @param b ͵ֽ + */ +#define UART3_SendByte(b) (R8_UART3_THR = b) + +/** + * @brief ڶֽڷ + * + * @param buf - ͵׵ַ + * @param l - ͵ݳ + */ +void UART3_SendString(uint8_t *buf, uint16_t l); + +/** + * @brief ڶȡֽ + * + * @return ȡĵֽ + */ +#define UART3_RecvByte() (R8_UART3_RBR) + +/** + * @brief ڶȡֽ + * + * @param buf - ȡݴŻ׵ַ + * + * @return ȡݳ + */ +uint16_t UART3_RecvString(uint8_t *buf); + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_UART_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_usbhost.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_usbhost.h new file mode 100644 index 0000000..78b92b7 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/CH58x_usbhost.h @@ -0,0 +1,349 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : CH57x_usbhost.h + * Author : WCH + * Version : V1.2 + * Date : 2021/11/17 + * Description + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ + +#ifndef __CH58x_USBHOST_H__ +#define __CH58x_USBHOST_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#if DISK_LIB_ENABLE + #if DISK_WITHOUT_USB_HUB + /* ʹUļϵͳU̹USBhub棬Ҫر涨 */ + #define FOR_ROOT_UDISK_ONLY + #endif + /* ʹUļϵͳ⣬Ҫ涨, ʹر */ + #define DISK_BASE_BUF_LEN 512 /* ĬϵĴݻСΪ512ֽ,ѡΪ20484096֧ijЩU,Ϊ0ֹ.Hļж建ӦópDISK_BASE_BUFָ */ +#endif + +// ӳ򷵻״̬ +#define ERR_SUCCESS 0x00 // ɹ +#define ERR_USB_CONNECT 0x15 /* ⵽USB豸¼,Ѿ */ +#define ERR_USB_DISCON 0x16 /* ⵽USB豸Ͽ¼,ѾϿ */ +#define ERR_USB_BUF_OVER 0x17 /* USB̫໺ */ +#define ERR_USB_DISK_ERR 0x1F /* USB洢ʧ,ڳʼʱUSB洢֧,ڶдпǴ𻵻ѾϿ */ +#define ERR_USB_TRANSFER 0x20 /* NAK/STALLȸ0x20~0x2F */ +#define ERR_USB_UNSUPPORT 0xFB /* ֵ֧USB豸*/ +#define ERR_USB_UNKNOWN 0xFE /* 豸*/ +#define ERR_AOA_PROTOCOL 0x41 /* Э汾 */ + +/*USB豸Ϣ,֧1豸*/ +#define ROOT_DEV_DISCONNECT 0 +#define ROOT_DEV_CONNECTED 1 +#define ROOT_DEV_FAILED 2 +#define ROOT_DEV_SUCCESS 3 +#define DEV_TYPE_KEYBOARD (USB_DEV_CLASS_HID | 0x20) +#define DEV_TYPE_MOUSE (USB_DEV_CLASS_HID | 0x30) +#define DEF_AOA_DEVICE 0xF0 +#define DEV_TYPE_UNKNOW 0xFF + +/* +Լ: USB豸ַ(οUSB_DEVICE_ADDR) +ֵַ 豸λ +0x02 Root-HUBµUSB豸ⲿHUB +0x1x Root-HUBµⲿHUBĶ˿xµUSB豸,xΪ1~n +*/ +#define HUB_MAX_PORTS 4 +#define WAIT_USB_TOUT_200US 800 // ȴUSBжϳʱʱ + +typedef struct +{ + uint8_t DeviceStatus; // 豸״̬,0-豸,1-豸δʼ,2-豸ʼöʧ,3-豸ҳʼöٳɹ + uint8_t DeviceAddress; // 豸USBַ + uint8_t DeviceSpeed; // 0Ϊ,0Ϊȫ + uint8_t DeviceType; // 豸 + uint16_t DeviceVID; + uint16_t DevicePID; + uint8_t GpVar[4]; // ͨñŶ˵ + uint8_t GpHUBPortNum; // ͨñ,HUBʾHUB˿ +} _RootHubDev; + +typedef struct +{ + UINT8 DeviceStatus; // 豸״̬,0-豸,1-豸δʼ,2-豸ʼöʧ,3-豸ҳʼöٳɹ + UINT8 DeviceAddress; // 豸USBַ + UINT8 DeviceSpeed; // 0Ϊ,0Ϊȫ + UINT8 DeviceType; // 豸 + UINT16 DeviceVID; + UINT16 DevicePID; + UINT8 GpVar[4]; // ͨñ +} _DevOnHubPort; // ٶ:1ⲿHUB,ÿⲿHUBHUB_MAX_PORTS˿(˲) + +extern _RootHubDev ThisUsbDev; +extern _DevOnHubPort DevOnHubPort[HUB_MAX_PORTS]; // ٶ:1ⲿHUB,ÿⲿHUBHUB_MAX_PORTS˿(˲) +extern uint8_t UsbDevEndp0Size; // USB豸Ķ˵0ߴ */ +extern uint8_t FoundNewDev; + +extern uint8_t *pHOST_RX_RAM_Addr; +extern uint8_t *pHOST_TX_RAM_Addr; + +extern _RootHubDev ThisUsb2Dev; +extern _DevOnHubPort DevOnU2HubPort[HUB_MAX_PORTS]; // ٶ:1ⲿHUB,ÿⲿHUBHUB_MAX_PORTS˿(˲) +extern uint8_t Usb2DevEndp0Size; // USB豸Ķ˵0ߴ */ +extern uint8_t FoundNewU2Dev; + +extern uint8_t *pU2HOST_RX_RAM_Addr; +extern uint8_t *pU2HOST_TX_RAM_Addr; + +#define pSetupReq ((PUSB_SETUP_REQ)pHOST_TX_RAM_Addr) +#define pU2SetupReq ((PUSB_SETUP_REQ)pU2HOST_TX_RAM_Addr) +extern uint8_t Com_Buffer[]; +extern uint8_t U2Com_Buffer[]; + +/* ΪUSB */ +extern const uint8_t SetupGetDevDescr[]; // ȡ豸*/ +extern const uint8_t SetupGetCfgDescr[]; // ȡ*/ +extern const uint8_t SetupSetUsbAddr[]; // USBַ*/ +extern const uint8_t SetupSetUsbConfig[]; // USB*/ +extern const uint8_t SetupSetUsbInterface[]; // USBӿ*/ +extern const uint8_t SetupClrEndpStall[]; // ˵STALL*/ + +extern const uint8_t SetupGetU2DevDescr[]; // ȡ豸*/ +extern const uint8_t SetupGetU2CfgDescr[]; // ȡ*/ +extern const uint8_t SetupSetUsb2Addr[]; // USBַ*/ +extern const uint8_t SetupSetUsb2Config[]; // USB*/ +extern const uint8_t SetupSetUsb2Interface[]; // USBӿ*/ +extern const uint8_t SetupClrU2EndpStall[]; // ˵STALL*/ + +/** + * @brief رROOT-HUB˿,ʵӲѾԶر,˴ֻһЩṹ״̬ + */ +void DisableRootHubPort(void); + +/** + * @brief ROOT-HUB״̬,ROOT-HUB˿ڵ豸¼ + * 豸γ,еDisableRootHubPort(),˿ڹر,¼,Ӧ˿ڵ״̬λ + * + * @return ERR_SUCCESSΪû,ERR_USB_CONNECTΪ⵽,ERR_USB_DISCONΪ⵽Ͽ + */ +uint8_t AnalyzeRootHub(void); + +/** + * @brief USBǰUSB豸ַ + * + * @param addr - USB豸ַ + */ +void SetHostUsbAddr(uint8_t addr); + +/** + * @brief õǰUSBٶ + * + * @param FullSpeed - USBٶ + */ +void SetUsbSpeed(uint8_t FullSpeed); + +/** + * @brief ⵽豸,λ,Ϊö豸׼,ΪĬΪȫ + */ +void ResetRootHubPort(void); + +/** + * @brief ʹROOT-HUB˿,ӦbUH_PORT_EN1˿,豸Ͽܵ·ʧ + * + * @return ERR_SUCCESSΪ⵽,ERR_USB_DISCONΪ + */ +uint8_t EnableRootHubPort(void); + +/** + * @brief ȴUSBж + * + * @return ERR_SUCCESS ݽջ߷ͳɹ,ERR_USB_UNKNOWN ݽջ߷ʧ + */ +uint8_t WaitUSB_Interrupt(void); + +/** + * @brief ,ĿĶ˵ַ/PID,ͬ־,20uSΪλNAKʱ(0,0xFFFF),0ɹ,ʱ/ + * ӳ,ʵӦ,Ϊṩٶ,ӦöԱӳŻ + * + * @param endp_pid - ƺ͵ַ, 4λtoken_pid, 4λǶ˵ַ + * @param tog - ͬ־ + * @param timeout - ʱʱ + * + * @return ERR_USB_UNKNOWN ʱӲ쳣 + * ERR_USB_DISCON 豸Ͽ + * ERR_USB_CONNECT 豸 + * ERR_SUCCESS + */ +uint8_t USBHostTransact(uint8_t endp_pid, uint8_t tog, uint32_t timeout); + +/** + * @brief ִпƴ,8ֽpSetupReq,DataBufΪѡշ + * + * @param DataBuf - Ҫպͷ,ôDataBufָЧڴź + * @param RetLen - ʵʳɹշܳȱRetLenָֽڱ + * + * @return ERR_USB_BUF_OVER IN״̬׶γ + * ERR_SUCCESS ݽɹ + */ +uint8_t HostCtrlTransfer(uint8_t *DataBuf, uint8_t *RetLen); + +/** + * @brief ƿƴ + * + * @param pReqPkt - ַ + */ +void CopySetupReqPkg(const uint8_t *pReqPkt); + +/** + * @brief ȡ豸, pHOST_TX_RAM_Addr + * + * @return ERR_USB_BUF_OVER ȴ + * ERR_SUCCESS ɹ + */ +uint8_t CtrlGetDeviceDescr(void); + +/** + * @brief ȡ, pHOST_TX_RAM_Addr + * + * @return ERR_USB_BUF_OVER ȴ + * ERR_SUCCESS ɹ + */ +uint8_t CtrlGetConfigDescr(void); + +/** + * @brief USB豸ַ + * + * @param addr - 豸ַ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlSetUsbAddress(uint8_t addr); + +/** + * @brief USB豸 + * + * @param cfg - ֵ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlSetUsbConfig(uint8_t cfg); + +/** + * @brief ˵STALL + * + * @param endp - ˵ַ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlClearEndpStall(uint8_t endp); + +/** + * @brief USB豸ӿ + * + * @param cfg - ֵ + * + * @return ERR_SUCCESS ɹ + */ +uint8_t CtrlSetUsbIntercace(uint8_t cfg); + +/** + * @brief USBܳʼ + */ +void USB_HostInit(void); +uint8_t EnumAllHubPort(void);// öROOT-HUB˿ⲿHUBĶUSB豸 +void SelectHubPort(uint8_t HubPortIndex); // HubPortIndex=0ѡָROOT-HUB˿,ѡָROOT-HUB˿ڵⲿHUBָ˿ +uint16_t SearchTypeDevice(uint8_t type); // ROOT-HUBԼⲿHUB˿ָ͵豸ڵĶ˿ں,˿ںΪ0xFFFFδ. +uint8_t SETorOFFNumLock(uint8_t *buf); // NumLockĵж + +void DisableRootU2HubPort(void); // رROOT-U2HUB˿,ʵӲѾԶر,˴ֻһЩṹ״̬ +uint8_t AnalyzeRootU2Hub(void); // ROOT-U2HUB״̬,ROOT-U2HUB˿ڵ豸¼ +// ERR_SUCCESSΪû,ERR_USB_CONNECTΪ⵽,ERR_USB_DISCONΪ⵽Ͽ +void SetHostUsb2Addr(uint8_t addr); // USBǰUSB豸ַ +void SetUsb2Speed(uint8_t FullSpeed); // õǰUSBٶ +void ResetRootU2HubPort(void); // ⵽豸,λӦ˿ڵ,Ϊö豸׼,ΪĬΪȫ +uint8_t EnableRootU2HubPort(void); // ʹROOT-HUB˿,ӦbUH_PORT_EN1˿,豸Ͽܵ·ʧ +void SelectU2HubPort(uint8_t HubPortIndex); // HubPortIndex=0ѡָROOT-HUB˿,ѡָROOT-HUB˿ڵⲿHUBָ˿ +uint8_t WaitUSB2_Interrupt(void); // ȴUSBж +// ,ĿĶ˵ַ/PID,ͬ־,20uSΪλNAKʱ(0,0xFFFF),0ɹ,ʱ/ +uint8_t USB2HostTransact(uint8_t endp_pid, uint8_t tog, UINT32 timeout); // endp_pid: 4λtoken_pid, 4λǶ˵ַ +uint8_t U2HostCtrlTransfer(uint8_t *DataBuf, uint8_t *RetLen); // ִпƴ,8ֽpSetupReq,DataBufΪѡշ +// Ҫպͷ,ôDataBufָЧڴź,ʵʳɹշܳȷرReqLenָֽڱ + +void CopyU2SetupReqPkg(const uint8_t *pReqPkt); // ƿƴ +uint8_t CtrlGetU2DeviceDescr(void); // ȡ豸, pHOST_TX_RAM_Addr +uint8_t CtrlGetU2ConfigDescr(void); // ȡ, pHOST_TX_RAM_Addr +uint8_t CtrlSetUsb2Address(uint8_t addr); // USB豸ַ +uint8_t CtrlSetUsb2Config(uint8_t cfg); // USB豸 +uint8_t CtrlClearU2EndpStall(uint8_t endp); // ˵STALL +uint8_t CtrlSetUsb2Intercace(uint8_t cfg); // USB豸ӿ + +void USB2_HostInit(void); // ʼUSB + +/*************************************************************/ + +/** + * @brief ʼָROOT-HUB˿ڵUSB豸 + * + * @return + */ +uint8_t InitRootDevice(void); + +/** + * @brief ȡHID豸,TxBuffer + * + * @return + */ +uint8_t CtrlGetHIDDeviceReport(uint8_t infc); + +/** + * @brief ȡHUB,Com_Buffer + * + * @return + */ +uint8_t CtrlGetHubDescr(void); + +/** + * @brief ѯHUB˿״̬,Com_Buffer + * + * @param HubPortIndex - ˿ں + * + * @return + */ +uint8_t HubGetPortStatus(uint8_t HubPortIndex); + +/** + * @brief HUB˿ + * + * @param HubPortIndex - ˿ں + * @param FeatureSelt - ˿ + * + * @return + */ +uint8_t HubSetPortFeature(uint8_t HubPortIndex, uint8_t FeatureSelt); + +/** + * @brief HUB˿ + * + * @param HubPortIndex - ˿ں + * @param FeatureSelt - ˿ + * + * @return + */ +uint8_t HubClearPortFeature(uint8_t HubPortIndex, uint8_t FeatureSelt); + +uint8_t InitRootU2Device(void); +uint8_t EnumAllU2HubPort(void); +uint16_t U2SearchTypeDevice(uint8_t type); +uint8_t U2SETorOFFNumLock(uint8_t *buf); + +uint8_t CtrlGetU2HIDDeviceReport(uint8_t infc); // HIDSET_IDLEGET_REPORT +uint8_t CtrlGetU2HubDescr(void); // ȡHUB,TxBuffer +uint8_t U2HubGetPortStatus(uint8_t HubPortIndex); // ѯHUB˿״̬,TxBuffer +uint8_t U2HubSetPortFeature(uint8_t HubPortIndex, uint8_t FeatureSelt); // HUB˿ +uint8_t U2HubClearPortFeature(uint8_t HubPortIndex, uint8_t FeatureSelt); // HUB˿ + +#ifdef __cplusplus +} +#endif + +#endif // __CH58x_USBHOST_H__ diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/inc/ISP583.h b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/ISP583.h new file mode 100644 index 0000000..e228c07 --- /dev/null +++ b/CH5xx_ble_firmware_library/StdPeriphDriver/inc/ISP583.h @@ -0,0 +1,190 @@ +/* CH583 Flash-ROM & Data-Flash */ +/* Website: http://wch.cn */ +/* Email: tech@wch.cn */ +/* Author: W.ch 2020.06 */ +/* V1.0 FlashROM library for USER/BOOT */ +/* for the target in USER code area on the chip divided into USER code area and BOOT area */ +/* ھûоƬĿΪû + ûбãIAPдҲбãû룩 */ + +/* Flash-ROM feature: + for store program code, support block erasing, dword and page writing, dword verifying, unit for Length is byte, + minimal quantity for write or verify is one dword (4-bytes), + 256 bytes/page for writing, FLASH_ROM_WRITE support one dword or more dword writing, but multiple of 256 is the best, + 4KB (4096 bytes) bytes/block for erasing, so multiple of 4096 is the best */ + +/* Data-Flash(EEPROM) feature: + for store data, support block erasing, byte and page writing, byte reading, + minimal quantity for write or read is one byte, + 256 bytes/page for writing, EEPROM_WRITE support one byte or more byte writing, but multiple of 256 is the best, + 0.25KB/4KB (256/4096 bytes) bytes/block for erasing, so multiple of 256 or 4096 is the best */ + + +#ifndef EEPROM_PAGE_SIZE +#define EEPROM_PAGE_SIZE 256 // Flash-ROM & Data-Flash page size for writing +#define EEPROM_BLOCK_SIZE 4096 // Flash-ROM & Data-Flash block size for erasing +#define EEPROM_MIN_ER_SIZE EEPROM_PAGE_SIZE // Data-Flash minimal size for erasing +//#define EEPROM_MIN_ER_SIZE EEPROM_BLOCK_SIZE // Flash-ROM minimal size for erasing +#define EEPROM_MIN_WR_SIZE 1 // Data-Flash minimal size for writing +#define EEPROM_MAX_SIZE 0x8000 // Data-Flash maximum size, 32KB +#endif +#ifndef FLASH_MIN_WR_SIZE +#define FLASH_MIN_WR_SIZE 4 // Flash-ROM minimal size for writing +#endif +#ifndef FLASH_ROM_MAX_SIZE +#define FLASH_ROM_MAX_SIZE 0x070000 // Flash-ROM maximum program size, 448KB +#endif + +#ifndef CMD_FLASH_ROM_SW_RESET +// CMD_* for caller from FlashROM or RAM, auto execute CMD_FLASH_ROM_SW_RESET before command + +#define CMD_FLASH_ROM_START_IO 0x00 // start FlashROM I/O, without parameter +#define CMD_FLASH_ROM_SW_RESET 0x04 // software reset FlashROM, without parameter +#define CMD_GET_ROM_INFO 0x06 // get information from FlashROM, parameter @Address,Buffer +#define CMD_GET_UNIQUE_ID 0x07 // get 64 bit unique ID, parameter @Buffer +#define CMD_FLASH_ROM_PWR_DOWN 0x0D // power-down FlashROM, without parameter +#define CMD_FLASH_ROM_PWR_UP 0x0C // power-up FlashROM, without parameter +#define CMD_FLASH_ROM_LOCK 0x08 // lock(protect)/unlock FlashROM data block, return 0 if success, parameter @StartAddr +// StartAddr: 0=unlock all, 1=lock boot code, 3=lock all code and data + +#define CMD_EEPROM_ERASE 0x09 // erase Data-Flash block, return 0 if success, parameter @StartAddr,Length +#define CMD_EEPROM_WRITE 0x0A // write Data-Flash data block, return 0 if success, parameter @StartAddr,Buffer,Length +#define CMD_EEPROM_READ 0x0B // read Data-Flash data block, parameter @StartAddr,Buffer,Length +#define CMD_FLASH_ROM_ERASE 0x01 // erase FlashROM block, return 0 if success, parameter @StartAddr,Length +#define CMD_FLASH_ROM_WRITE 0x02 // write FlashROM data block, minimal block is dword, return 0 if success, parameter @StartAddr,Buffer,Length +#define CMD_FLASH_ROM_VERIFY 0x03 // read FlashROM data block, minimal block is dword, return 0 if success, parameter @StartAddr,Buffer,Length +#endif + +#define ROM_CFG_MAC_ADDR 0x7F018 // address for MAC address information +#define ROM_CFG_BOOT_INFO 0x7DFF8 // address for BOOT information + +/** + * @brief execute Flash/EEPROM command, caller from FlashROM or RAM + * + * @param cmd - CMD_* for caller from FlashROM or RAM. + * @param StartAddr - Address of the data to be process. + * @param Buffer - Pointer to the buffer where data should be process, Must be aligned to 4 bytes. + * @param Length - Size of data to be process, in bytes. + * + * @return 0-SUCCESS (!0)-FAILURE + */ +extern uint32_t FLASH_EEPROM_CMD( uint8_t cmd, uint32_t StartAddr, void *Buffer, uint32_t Length ); + +/** + * @brief start FlashROM I/O + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define FLASH_ROM_START_IO( ) FLASH_EEPROM_CMD( CMD_FLASH_ROM_START_IO, 0, NULL, 0 ) + +/** + * @brief software reset FlashROM + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define FLASH_ROM_SW_RESET( ) FLASH_EEPROM_CMD( CMD_FLASH_ROM_SW_RESET, 0, NULL, 0 ) + +/** + * @brief get 6 bytes MAC address + * + * @param Buffer - Pointer to the buffer where data should be stored, Must be aligned to 4 bytes. + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define GetMACAddress(Buffer) FLASH_EEPROM_CMD( CMD_GET_ROM_INFO, ROM_CFG_MAC_ADDR, Buffer, 0 ) + +/** + * @brief get 8 bytes BOOT information + * + * @param Buffer - Pointer to the buffer where data should be stored, Must be aligned to 4 bytes. + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define GET_BOOT_INFO(Buffer) FLASH_EEPROM_CMD( CMD_GET_ROM_INFO, ROM_CFG_BOOT_INFO, Buffer, 0 ) + +/** + * @brief get 64 bit unique ID + * + * @param Buffer - Pointer to the buffer where data should be stored, Must be aligned to 4 bytes. + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define GET_UNIQUE_ID(Buffer) FLASH_EEPROM_CMD( CMD_GET_UNIQUE_ID, 0, Buffer, 0 ) + +/** + * @brief power-down FlashROM + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define FLASH_ROM_PWR_DOWN( ) FLASH_EEPROM_CMD( CMD_FLASH_ROM_PWR_DOWN, 0, NULL, 0 ) + +/** + * @brief power-up FlashROM + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define FLASH_ROM_PWR_UP( ) FLASH_EEPROM_CMD( CMD_FLASH_ROM_PWR_UP, 0, NULL, 0 ) + +/** + * @brief read Data-Flash data block + * + * @param StartAddr - Address of the data to be read. + * @param Buffer - Pointer to the buffer where data should be stored, Must be aligned to 4 bytes. + * @param Length - Size of data to be read, in bytes. + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define EEPROM_READ(StartAddr,Buffer,Length) FLASH_EEPROM_CMD( CMD_EEPROM_READ, StartAddr, Buffer, Length ) + +/** + * + * @param StartAddr - Address of the data to be erased. + * @param Length - Size of data to be erased, in bytes. + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define EEPROM_ERASE(StartAddr,Length) FLASH_EEPROM_CMD( CMD_EEPROM_ERASE, StartAddr, NULL, Length ) + +/** + * @brief write Data-Flash data block + * + * @param StartAddr - Address of the data to be written. + * @param Buffer - Pointer to the source buffer, Must be aligned to 4 bytes. + * @param Length - Size of data to be written, in bytes. + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define EEPROM_WRITE(StartAddr,Buffer,Length) FLASH_EEPROM_CMD( CMD_EEPROM_WRITE, StartAddr, Buffer, Length ) + +/** + * @brief erase FlashROM block + * + * @param StartAddr - Address of the data to be erased. + * @param Length - Size of data to be erased, in bytes. + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define FLASH_ROM_ERASE(StartAddr,Length) FLASH_EEPROM_CMD( CMD_FLASH_ROM_ERASE, StartAddr, NULL, Length ) + +/** + * @brief write FlashROM data block, minimal block is dword. + * + * @param StartAddr - Address of the data to be written. + * @param Buffer - Pointer to the source buffer, Must be aligned to 4 bytes. + * @param Length - Size of data to be written, in bytes. + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define FLASH_ROM_WRITE(StartAddr,Buffer,Length) FLASH_EEPROM_CMD( CMD_FLASH_ROM_WRITE, StartAddr, Buffer, Length ) + +/** + * @brief verify FlashROM data block, minimal block is dword. + * + * @param StartAddr - Address of the data to verify. + * @param Buffer - Pointer to the source buffer, Must be aligned to 4 bytes. + * @param Length - Size of data to verify, in bytes. + * + * @return 0-SUCCESS (!0)-FAILURE + */ +#define FLASH_ROM_VERIFY(StartAddr,Buffer,Length) FLASH_EEPROM_CMD( CMD_FLASH_ROM_VERIFY, StartAddr, Buffer, Length ) + diff --git a/CH5xx_ble_firmware_library/StdPeriphDriver/libISP583.a b/CH5xx_ble_firmware_library/StdPeriphDriver/libISP583.a new file mode 100644 index 0000000000000000000000000000000000000000..4191b26b804f846f4d9a10f22f41904cce449e42 GIT binary patch literal 5374 zcmcJTdr(x@9mmhIcXwGpc`W#7Y`U@*U$EIHk2bLoci9p*#G;6gnFhF!f{$PkUmZ;s zkSbbj%mxjnX$@@?{$jIdI~{qpT(`|`Hrpa0@z-p&J6-Ap<#CKL z106kIbrkvw7JT>VlBb@0`f;z1-B;dXkD!W*1@TnrLO~UHJSBoGD=6YouXjG`&VACu ziWbaw=Q^!*d?e8kP{+l-+ikY&>}<2yZMQp=A(Y4c*y}IUXf!kcrD@nd*?q-WO$v)> zS0p@x#W5qZXr(}!&!W8&&@fBPuLDhiWf~bZK-3(N}z-JA|*#=+@u?fj@Rht{gdC^o99_wsqXDut78*4QMD}q}| z4^7v!hvX5LCU~)kz2ggA%^7>_q^|az+Rc+McA0d6aq_lWjY&sywq+xolVW?K&kXOi z{d{qI=N4DNs*`;!S<%t_r$Ert40!Y}@u)*E6bhq@x`8Co}d%}I9-g$M2w z9B%Q20^vb%EWy@)jnV!4d9C139@~ARIYSC)ryE(&P{cxeu6{hHwRg+KlZVk`X3{+$ zUFFfvH+?4Ep}5et@V$1Qyf-X$>0()D^)YsTwi-P<=e=6Q>&tr@G|khcfW;hUEmEk> zH3nsxI9nr^8ki*|%xaHW(!=a~$6oQtzYNDTda1s|ul|$AVy0ar-N;6la4$I2zAJIkEJR|yS+<)qM?NF+&cUM)m3JSXPTXktjgMkndV%p!)jADSm9UPXC-!P zOb9XU7j|JrIz|kW);^#m+M5}p?OJV8gw0$siG7XOD(G}#N3@K@W|Lkn#2+EvCTJ(| zIzi_UUl;VF#J?AG0dc>e3yEu@gO=Dl(wCyclGuFG_k?&U>BoimGSX*+csc3Yg03cg zLC_e>aY5ISep{&5Kzf~^H0%+ii}Wu99V9&>A>|Bq<>DlNY$%J-v(135a%CqGPAC%t6Oc)RnCq)q5XKZg#x z6F+YJAY`Wt{0_?YJM54)L>#!l;lfWuBGG{YPUJWda7HTFP!ad^JCWl=Z6}I35pW`a zp7>n|xNwOJrCo@+P{xJJT&S9Z6nCT}ktnWpM|x<(o!!WGqkwvj` z2)=q>F!ztbe~f;pfw|vJ%zZHT@ecF)VD8hWdbB>6``h8G;{tOZ?=|HdJ5-n& znEU4uK)qF7bPYC&7?)So1=LgLFhiTv7^}*c7 z{>bZtxxZb=2XlWPe6>E9``E|$dV#tB8GLnqz}&|^#_NE&pNn^2%?EQI`xws$bH5(G zIzM3U?}x8GKVa@3fUmAEnEP)D`C#rJhp(<9nER&$AI$w;_-cQ^+`kH6?GKpyH{q-O z0ds!>Jt9eZ`k^+BjwC|_nFAcFa0xh0;SJz;h4;}ZD?#B4bjlp3@MmCb*3<^+$V!YL zTS(7jQj)@p1+EnM1#q&G(*efDPwk+_-LcH3H*@40phGF3U32{UEy8Gd062+0v{sI{D$HmMZQ^K ze0wCRb^72>Rs1XP(-j^BTNJ({utd)=_s4^=Xw>k1!E0uNjf$U7ocT?K3&9x*`@z!{ zUId<@@M7XDwa?4p&s6++V!jqX0?$(X=a8??c@unm%u?F}R>$}nab~9C{{eoM!pDiT z@Q0gPH++2fQacM@z4iipo8n&v+ZB$XXE@2Ba1vO3pr?S9|1@FKh +#include +#include "wang.h" +// The left- and rightwards animations work, effectively, the same +// each has a one-screen (44 pixel) dead zone at the start/end + + +// up and down are one-page only + +// fixed just steps through each page +// snowflake idk + +// picture i'm not sure yet but we'll find out +// laser same + +void rotate_vertical(uint16_t *fb, int width, int count) { + for (int i = 0; i < width; i++) { + fb[i] = (fb[i] << count) | (fb[i] >> (11 - count)); + } +} + +void render_moved(uint16_t *fb, int index, int x, int rotate) { + for (int i = 0; i < 44; i++) fb[i] = 0; + + // Read in the initial buffer. + // The image starts at 0, and continues until {width}. + // We trust the wang code not to overread. + if (x < 0) { + if (x < -44) return; + wang_read(index, 0, 44 + x, fb - x); + } else { + wang_read(index, x, 44, fb); + } + if (rotate < 0) rotate = 11 - rotate; + if (rotate > 11) rotate = rotate % 11; + rotate_vertical(fb, 44, rotate); +} + +int apply_modifiers(uint16_t *fb, int frame, int blink, int marquee) { + int can_restart_blink = !blink; + int can_restart_marquee = !marquee; + + if (frame % 6 > 3 && blink) { + for (int i = 0; i < 44; i++) fb[i] = 0; + can_restart_blink = frame % 6 == 5; + } + + if (marquee) { + // marquee pattern is 00010001 + uint16_t marquee = (0x11 << ((frame / 2) % 4)); + // Apply top row + for (int i = 0; i < 44; i++) { + if (marquee & (1 << (i % 8))) fb[i] ^= 1; + } + for (int i = 1; i < 10; i++) { + if (marquee & (1 << ((i - 1) % 8))) fb[43] ^= (1 << i); + } + // The marquee pattern is now shifted by 5 bits. + for (int i = 0; i < 44; i++) { + if (marquee & (1 << ((i + 5) % 8))) fb[43 - i] ^= (1 << 10); + } + for (int i = 1; i < 10; i++) { + if (marquee & (1 << ((i + 4) % 8))) fb[0] ^= (1 << (10 - i)); + } + + can_restart_marquee = frame % 8 == 7; + } + + return can_restart_blink && can_restart_marquee; +} + +int anim_render(uint16_t *fb, int index, int frame) { + int im_width = flash_header.widths[index] * 8; + if (im_width % 44 < 5) { + im_width = im_width - (im_width % 44); + } + + int speed = flash_header.speed_and_mode[index] >> 4; + frame /= 8 - speed; + + int animation = flash_header.speed_and_mode[index] & 0xf; + + int x = 0; + int rotate = 0; + int can_restart_lr = 1; + int can_restart_rotate = 1; + + switch (animation) { + case 0: // left + x = -44 + frame; + can_restart_lr = 0; + if (x >= im_width - 1) can_restart_lr = 1; + break; + case 1: // right + x = im_width + 44 - frame; + can_restart_lr = 0; + if (x < -43) can_restart_lr = 1; + break; + case 2: + rotate = frame % 11; + can_restart_rotate = rotate == 11; + break; + case 3: + rotate = 10 -(frame % 11); + can_restart_rotate = rotate == 0; + break; + case 4: + if (im_width > 44) { + can_restart_lr = 0; + if (frame > (im_width / 44) * 16) { + can_restart_lr = 1; + } + + x = (frame / 16) * 44; + } + + if (im_width - x < 44) { + x -= 22 - (im_width - x) / 2; + } + } + + render_moved(fb, index, x, rotate); + int can_restart_mods = apply_modifiers(fb, frame, flash_header.flash_map & (1 << index), flash_header.marquee_map & (1 << index)); + + return can_restart_lr && can_restart_rotate && can_restart_mods; +} diff --git a/src/ble.c b/src/ble.c new file mode 100644 index 0000000..a6748d9 --- /dev/null +++ b/src/ble.c @@ -0,0 +1,200 @@ +#include +#include + +#include "CH58x_common.h" +#include "CH58x_sys.h" +#include "CH58xBLE_LIB.h" +#include "led.h" +#include "wang.h" + +int ble_on = 0; +int ble_connected = 0; +void ble_toggle(void) { + uint8_t ble_mode = ble_on ? FALSE : TRUE; + GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(ble_mode), &ble_mode); + ble_on = !ble_on; +} + +// We use TMOS only for BLE things and wrap the rest. +static __attribute__((aligned(4), section(".noinit"))) +uint8_t BLE_BUF[1024 * 6]; + +static void ble_calib_cb(void) { + Calibration_LSI(Level_128); +} + +static uint8_t periph_task = INVALID_TASK_ID; + +static uint16_t peripheral_task(uint8_t task_id, uint16_t events) { + if (events & SYS_EVENT_MSG) { + uint8_t *pMsg = tmos_msg_receive(periph_task); + if (pMsg) { + // process the message. noop. + tmos_msg_deallocate(pMsg); + } + + return events ^ SYS_EVENT_MSG; + } + + return 0; +} + +static void gap_onParamUpdate(uint16_t a, uint16_t b, uint16_t c, uint16_t d) { +} +static void gap_onStateChange(gapRole_States_t a, gapRoleEvent_t *b) { +} + +uint8_t advertData[] = { + 0x02, + GAP_ADTYPE_FLAGS, + GAP_ADTYPE_FLAGS_GENERAL | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED, + + 0x03, + GAP_ADTYPE_16BIT_MORE, + LO_UINT16(0xFEE0), + HI_UINT16(0xFEE0), +}; + +uint8_t scanRspData[31] = { + 0x05, + GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE, + LO_UINT16(100), + HI_UINT16(100), + LO_UINT16(200), + HI_UINT16(200), + + 0x02, + GAP_ADTYPE_POWER_LEVEL, + 6, + + 19, + GAP_ADTYPE_LOCAL_NAME_COMPLETE, + 'L', 'S', 'L', 'E', 'D', ' ', 'B', 'a', 'd', 'g', 'e', ' ', 'W', 'i', 't', 'c', 'h', 0, + + 0, 0, 0, 0, +}; + +static gapRolesBroadcasterCBs_t broadcast_handlers = { + 0, + 0, +}; +static gapRolesCBs_t gap_handlers = { + gap_onStateChange, + 0, + gap_onParamUpdate, +}; + +static gapBondCBs_t bond_managers = { + 0, + 0, +}; + +static const uint16_t service_uuid = 0xFEE0; +static const gattAttrType_t service = {2, (uint8_t *) &service_uuid}; + +static const uint16_t rx_char_uuid = 0xFEE1; +static uint8_t rx_char_props = GATT_PROP_WRITE; +static uint8_t rx_char_val[16]; + +static gattAttribute_t attr_table[] = { + {{2, &primaryServiceUUID}, GATT_PERMIT_READ, 0, &service}, + {{2, &characterUUID}, GATT_PERMIT_READ, 0, &rx_char_props}, + {{2, &rx_char_uuid}, GATT_PERMIT_WRITE, 0, &rx_char_val}, +}; + +uint16_t blefb[44] = {0}; + +static bStatus_t write_handler(uint16_t conn_handle, gattAttribute_t *pAttr, uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method) { + if (gattPermitAuthorWrite(pAttr->permissions)) return ATT_ERR_INSUFFICIENT_AUTHOR; + + uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]); + if (uuid != rx_char_uuid) return ATT_ERR_ATTR_NOT_FOUND; + + wang_rx(pValue, len); + + return SUCCESS; +} + +static gattServiceCBs_t service_handlers = { + 0, + write_handler, + 0, +}; + +void ble_init(void) { + bleConfig_t cfg = {0}; + + // BLE heap + cfg.MEMAddr = (uint32_t) BLE_BUF; + cfg.MEMLen = (uint32_t) sizeof(BLE_BUF); + + // not using the bonding information, as we do not support _bonding_. + // This should be changed. but. you know. + + // TODO: magic numbers + // amount of buffered packets + cfg.BufNumber = (512 / 23); + // maximum data length of each packet + cfg.BufMaxLen = (64 + 4); + + cfg.TxNumEvent = 1; + cfg.TxPower = LL_TX_POWEER_6_DBM; + cfg.ConnectNumber = (1 & 3) | (1 << 2); + cfg.SelRTCClock = 1 << 7; + cfg.rcCB = ble_calib_cb; + + uint8_t m[6]; + GetMACAddress(m); + memcpy(cfg.MacAddr, m, 6); + + BLE_LibInit(&cfg); + + sys_safe_access_enable(); + R8_CK32K_CONFIG &= ~(RB_CLK_OSC32K_XT | RB_CLK_XT32K_PON); + sys_safe_access_enable(); + R8_CK32K_CONFIG |= RB_CLK_INT32K_PON; + sys_safe_access_disable(); + + Calibration_LSI(Level_128); + RTC_InitTime(2020, 1, 1, 0, 0, 0); + TMOS_TimerInit(0); + + + periph_task = TMOS_ProcessEventRegister(peripheral_task); + + GAPRole_PeripheralInit(); + + // the GAP parameters all kinda suck to set. sooo + #define SET_PARAM(a, b, c, d) do { static c val = d; a(b, sizeof(val), &val); } while(0) + SET_PARAM(GAPRole_SetParameter, GAPROLE_ADVERT_ENABLED, uint8_t, FALSE); + GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, 31, scanRspData); + GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData); + SET_PARAM(GAPRole_SetParameter, GAPROLE_MIN_CONN_INTERVAL, uint16_t, 6); + SET_PARAM(GAPRole_SetParameter, GAPROLE_MAX_CONN_INTERVAL, uint16_t, 500); + + GGS_SetParameter(GGS_DEVICE_NAME_ATT, 20, "LED Badge Witch\0\0\0\0\0\0\0"); + GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, 100); + GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, 200); + + SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_DEFAULT_PASSCODE, uint32_t, 0); + SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_PAIRING_MODE, uint8_t, GAPBOND_PAIRING_MODE_NO_PAIRING); + SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_MITM_PROTECTION, uint8_t, FALSE); + SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_IO_CAPABILITIES, uint8_t, GAPBOND_IO_CAP_DISPLAY_ONLY); + SET_PARAM(GAPBondMgr_SetParameter, GAPBOND_PERI_BONDING_ENABLED, uint8_t, FALSE); + + GAPRole_BroadcasterSetCB(&broadcast_handlers); + + GGS_AddService(GATT_ALL_SERVICES); + GATTServApp_AddService(GATT_ALL_SERVICES); + + GAPRole_PeripheralStartDevice(periph_task, &bond_managers, &gap_handlers); + + GATTServApp_RegisterService(attr_table, GATT_NUM_ATTRS(attr_table), GATT_MAX_ENCRYPT_KEY_SIZE, &service_handlers); +} + +struct connection_data { + uint16_t handle; + uint16_t interval; + uint16_t slave_latency; + uint16_t timeout; +}; diff --git a/src/ble.h b/src/ble.h new file mode 100644 index 0000000..bd7d86c --- /dev/null +++ b/src/ble.h @@ -0,0 +1,7 @@ +#pragma once + +void ble_init(void); +extern int ble_on; +extern int ble_connected; + +void ble_toggle(void); diff --git a/src/button.c b/src/button.c new file mode 100644 index 0000000..587cd81 --- /dev/null +++ b/src/button.c @@ -0,0 +1,79 @@ +#include +#include + +#include "CH58x_common.h" +#include "CH58x_sys.h" +#include "CH58xBLE_LIB.h" + + +volatile int button_count[2] = {15, 0}; +volatile int button_pressed[2] = {1, 0}; + +static void handle_button(int i, int state) { + if (state && button_count[i] < 16) button_count[i]++; + else if (!state && button_count[i] > 0) button_count[i]--; + + if (button_count[i] == 16) button_pressed[i] = 1; + else if (button_count[i] == 0) button_pressed[i] = 0; +} + +__INTERRUPT +__HIGH_CODE +void TMR3_IRQHandler(void) { + handle_button(0, !!GPIOA_ReadPortPin(GPIO_Pin_1)); + handle_button(1, !GPIOB_ReadPortPin(GPIO_Pin_22)); + + TMR3_ClearITFlag(TMR0_3_IT_CYC_END); +} + +void button_init(void) +{ + GPIOA_ModeCfg(GPIO_Pin_1, GPIO_ModeIN_PD); + GPIOB_ModeCfg(GPIO_Pin_22, GPIO_ModeIN_PU); + + DelayMs(100); + if (!GPIOB_ReadPortPin(GPIO_Pin_22)) { + asm volatile("j 0x00"); + } + + TMR3_TimerInit(FREQ_SYS / 1000); // 1ms timer + TMR3_ITCfg(ENABLE, TMR0_3_IT_CYC_END); + PFIC_EnableIRQ(TMR3_IRQn); + + GPIOA_ModeCfg(GPIO_Pin_5, GPIO_ModeIN_Floating); + ADC_ExtSingleChSampInit(SampleFreq_3_2, ADC_PGA_0); + ADC_DataCalib_Rough(); + ADC_ChannelCfg(1); + GPIOA_ModeCfg(GPIO_Pin_0, GPIO_ModeIN_PU); + GPIOA_ModeCfg(GPIO_Pin_2, GPIO_ModeIN_PD); +} + +int get_battery_percentage(void) { + uint32_t adc = 0; + + for (int i = 0; i < 24; i++) { + adc += ADC_ExcutSingleConver(); + } + + adc /= 24; + + // We have a rough voltage divider setup + + float v_adc = (adc / 4096.0 * 2.1); + float v_bat = (v_adc / 100.0 * (182.0 + 100.0)); + + if (v_bat < 3.3) + return 0; + + if (v_bat > 4.2) return 100; + + return ((v_bat - 3.3) / (4.2 - 3.3)) * 100; +} + +int is_charging(void) { + return !GPIOA_ReadPortPin(GPIO_Pin_0); +} + +int is_plugged(void) { + return !!GPIOA_ReadPortPin(GPIO_Pin_2); +} diff --git a/src/button.h b/src/button.h new file mode 100644 index 0000000..63a7beb --- /dev/null +++ b/src/button.h @@ -0,0 +1,8 @@ +#pragma once + +void button_init(void); +extern volatile int button_pressed[2]; + +int get_battery_percentage(void); +int is_charging(void); +int is_plugged(void); diff --git a/src/cdc.c b/src/cdc.c new file mode 100644 index 0000000..f164b5b --- /dev/null +++ b/src/cdc.c @@ -0,0 +1,26 @@ +#include +#include + +#include "CH58x_common.h" +#include "CH58x_sys.h" +#include "CH58xBLE_LIB.h" +#include "usb/core.h" +#include "wang.h" + +void cdc_tick(void) { + static uint8_t buf[64]; + static int count = -1; + count = usb_recv(1, buf, 64); + if (count <= 0) return; + + wang_rx(buf, count); +} + +void hid_tick(void) { + static uint8_t buf[64]; + static int count = -1; + count = usb_recv(2, buf, 64); + if (count <= 0) return; + + wang_rx(buf, count); +} diff --git a/src/cdc.h b/src/cdc.h new file mode 100644 index 0000000..d854b28 --- /dev/null +++ b/src/cdc.h @@ -0,0 +1,4 @@ +#pragma once + +void cdc_tick(void); +void hid_tick(void); diff --git a/src/img/menu.xbm b/src/img/menu.xbm new file mode 100644 index 0000000..82a0f09 --- /dev/null +++ b/src/img/menu.xbm @@ -0,0 +1,9 @@ +#define menu_width 44 +#define menu_height 11 +static unsigned char menu_bits[] = { + 0x01, 0x3c, 0x80, 0x07, 0x70, 0x08, 0xfe, 0xdb, 0x7f, 0xfb, 0xaf, 0x07, + 0xfe, 0xdb, 0x7b, 0x7b, 0xaf, 0x07, 0xee, 0xdb, 0x72, 0x5b, 0xad, 0x07, + 0xe6, 0xdb, 0x69, 0x6b, 0xab, 0x07, 0x02, 0xda, 0x73, 0xeb, 0xab, 0x07, + 0xe6, 0xdb, 0x69, 0xeb, 0xab, 0x07, 0xee, 0xdb, 0x72, 0xdb, 0xad, 0x07, + 0xfe, 0xdb, 0x7b, 0x3b, 0xae, 0x07, 0xfe, 0xdb, 0x7f, 0xfb, 0xaf, 0x07, + 0x01, 0x3c, 0x80, 0x07, 0x30, 0x00 }; diff --git a/src/led.c b/src/led.c new file mode 100644 index 0000000..472288b --- /dev/null +++ b/src/led.c @@ -0,0 +1,190 @@ +#include +#include +#include "CH58x_common.h" +#include "CH58x_sys.h" +#include "CH58xBLE_LIB.h" +#include "led.h" + +uint32_t pa_mask; +uint32_t pb_mask; +#define A(v) v +#define B(v) (v | 0x80) +uint8_t pins[23] = { + A(15), + B(18), + B(0), + B(7), + A(12), + A(10), + A(11), + B(9), + B(8), + B(15), + B(14), + B(13), + B(12), + B(5), + A(4), + B(3), + B(4), + B(2), + B(1), + B(6), + B(21), + B(20), + B(19), +}; + +// Set up the pa_mask and pb_mask values. +// These are used to mask out the GPIOs we control from those we do not. +void display_init(void) { + for (int i = 0; i < 23; i++) { + if (pins[i] & 0x80) { + pb_mask |= (1 << (pins[i] & 0x7F)); + } else { + pa_mask |= (1 << (pins[i] & 0x7F)); + } + } +} + +// Commit a struct row_buf to the display. +__HIGH_CODE +void display_commit(struct row_buf *b) { + // To avoid as much artifacting, we first tri-state all our pins. + // This is done in order of PD_DRV (which controls either the pull-down + // or the current drive capability). + R32_PA_PD_DRV = (R32_PA_PD_DRV & ~pa_mask); + R32_PB_PD_DRV = (R32_PB_PD_DRV & ~pb_mask); + + // We then disable drive any of the previously-output GPIOs low. + R32_PA_OUT = (R32_PA_OUT & ~pa_mask); + R32_PB_OUT = (R32_PB_OUT & ~pb_mask); + + // Now we set all GPIOs as inputs, tristating them. + R32_PA_DIR = (R32_PA_DIR & ~pa_mask); + R32_PB_DIR = (R32_PB_DIR & ~pb_mask); + + // We set the nicessary pins to output (driving them low), + R32_PA_DIR |= b->pa_dir; + // Configure the current drive (which is a noop, as none of the pins should be driven high).. + R32_PA_PD_DRV |= b->pa_drv; + + // (Do the same on GPIO bank B) + R32_PB_DIR |= b->pb_dir; + R32_PB_PD_DRV |= b->pb_drv; + + // And drive the one pin high, setting up the entire matrix. + R32_PA_OUT |= b->pa_out; + R32_PB_OUT |= b->pb_out; + + // This is most plausibly overkill, and needs some testing. +} + +void display_make_buf(uint16_t *fb, struct row_buf *b, int i) { + #define SET_OUT(pin) if (pin & 0x80) { b->pb_dir |= (1 << (pin & 0x7F)); } else { b->pa_dir |= (1 << pin); } + #define SET_HI(pin) if (pin & 0x80) { b->pb_out |= (1 << (pin & 0x7F)); } else { b->pa_out |= (1 << pin); } + + b->pa_dir = 0; + b->pa_out = 0; + b->pa_drv = 0; + b->pb_dir = 0; + b->pb_out = 0; + b->pb_drv = 0; + + uint16_t col1 = fb[i * 2]; + uint16_t col2 = fb[i * 2 + 1]; + + // On the first column, the lower two pins are swapped. Fix this in software. + if (i == 0) { + uint16_t bit = col1 & 1; + col1 = (col1 & 0xFFFE) | (col2 & 1); + col2 = (col2 & 0xFFFE) | bit; + } + + // The LEDs are written in a zig-zag pattern. + uint32_t merged = 0; + merged |= ((col1 & 1) << 22); + merged |= ((col2 & 1) << 23); + for (int j = 0; j < 11; j++) { + col1 >>= 1; + col2 >>= 1; + + merged >>= 2; + merged |= (col1 & 1) << 22; + merged |= (col2 & 1) << 23; + } + + // In this LED matrix, we set row N as a high output, + // then set all other lines to tristate or low, depending + // on whether they should be on or off. + int count = 0; + for (int j = 0; j < 23; j++) { + if (j == i) continue; + + if (merged & 1) { + count++; + SET_OUT(pins[j]); + } + merged >>= 1; + } + + // Set the charlieplexed row. + // This avoids setting it if the row is empty, + // to decrease artifacting. + if (count) { + SET_OUT(pins[i]); SET_HI(pins[i]); + } + + // If we have >5 LEDs on, enable the 20mA drive mode. + if (count > 5) { + b->pa_drv = b->pa_dir; + b->pb_drv = b->pb_dir; + } +} + +void display_make_buf_all(uint16_t *fb, struct row_buf *b) { + for (int i = 0; i < 22; i++) { + display_make_buf(fb, b + i, i); + } +} + +int display_brightness = 0; +static struct row_buf display_screens[22 * 2]; +static struct row_buf blank = {0}; + +struct row_buf *display_screen = display_screens; +static struct row_buf *display_screen_render = display_screens + 22; + +void display_flip(void) { + struct row_buf *new_render = display_screen == display_screens ? (display_screens + 22) : display_screens; + struct row_buf *old_render = display_screen; + + __atomic_exchange(&display_screen, &display_screen_render, &display_screen_render, __ATOMIC_RELAXED); +} + +__INTERRUPT +__HIGH_CODE +void TMR0_IRQHandler(void) +{ + static int cur_index; + static int cur_bness; + + // If this is a spurious interrupt, skip it. + if (!TMR0_GetITFlag(TMR0_3_IT_CYC_END)) return; + TMR0_ClearITFlag(TMR0_3_IT_CYC_END); + + cur_index += 1; + if (cur_index > 21) { + cur_index = 0; + cur_bness++; + if (cur_bness == 16) { + cur_bness = 0; + } + } + + if (cur_bness > display_brightness) { + display_commit(&blank); + } else { + display_commit(display_screen_render + cur_index); + } +} diff --git a/src/led.h b/src/led.h new file mode 100644 index 0000000..298d8ca --- /dev/null +++ b/src/led.h @@ -0,0 +1,24 @@ +#pragma once + +extern uint32_t pa_mask; +extern uint32_t pb_mask; + +struct row_buf { + uint32_t pa_dir; + uint32_t pa_out; + uint32_t pa_drv; + uint32_t pb_dir; + uint32_t pb_out; + uint32_t pb_drv; +}; + + +void display_commit(struct row_buf *b); +void display_make_buf(uint16_t *fb, struct row_buf *b, int i); +void display_make_buf_all(uint16_t *fb, struct row_buf *b); + +extern int display_brightness; +extern struct row_buf *display_screen; + +void display_flip(void); +void display_init(void); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..49f9b8d --- /dev/null +++ b/src/main.c @@ -0,0 +1,136 @@ +#include +#include + +#include "CH58x_common.h" +#include "CH58x_sys.h" +#include "CH58xBLE_LIB.h" +#include "usb/core.h" +#include "led.h" +#include "button.h" +#include "menu.h" +#include "ble.h" +#include "cdc.h" +#include "wang.h" + +int anim_render(uint16_t *fb, int index, int frame); + +enum usb_control_resp bl_handler(enum usb_control_state state) { + if (usb_control_request.bmRequestType != 0x60) return USB_CONTROL_RESP_PASS; + + if (usb_control_request.bRequest != 0x69) + return USB_CONTROL_RESP_STALL; + + asm volatile("j 0x00"); + return USB_CONTROL_RESP_ACK; +} + + +int btn1_was_pressed = 1; +int btn2_was_pressed = 0; +int btn1_hold = 0; +int btn2_hold = 0; + +int image_index = 0; + +int frame = 0; +int subframe = 0; + +int main_handler(void) { + + #define TIMER(btn0, btn1, v, count) if (button_pressed[0] != btn0 || button_pressed[1] != btn1) { v = 0; } else if (v < count) { v += 1; } else + static int menu_timer = 0; + TIMER(1, 0, menu_timer, 500) { + display_brightness = (display_brightness + 15) % 16; + menu_timer = 0; + menu_switch(); + return 1; + } + + if (button_pressed[0] && !btn1_was_pressed) { + display_brightness = (display_brightness + 1) % 16; + } + + if (button_pressed[1] && !btn2_was_pressed) { + if (flash_header_valid) { + image_index++; + if (!flash_header.widths[image_index]) image_index = 0; + frame = 0; + subframe = 99999; + } + display_flip(); + } + + subframe++; + if (subframe > 32 && flash_header_valid) { + // approximately 60fps + subframe = 0; + + uint16_t fb[44]; + if (anim_render(fb, image_index, frame)) { + frame = 0; + } else { + frame++; + } + display_make_buf_all(fb, display_screen); + display_flip(); + } + + btn2_was_pressed = button_pressed[1]; + btn1_was_pressed = button_pressed[0]; + + return 0; +} + +int main() +{ + SetSysClock(CLK_SOURCE_PLL_60MHz); + + button_init(); + + display_init(); + + WWDG_SetCounter(0x7F); + WWDG_ResetCfg(ENABLE); + + TMR0_TimerInit((FREQ_SYS / 16000) / 2); + TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END); + PFIC_EnableIRQ(TMR0_IRQn); + + TMR3_TimerInit(FREQ_SYS / 200); + TMR3_ITCfg(ENABLE, TMR0_3_IT_CYC_END); + PFIC_EnableIRQ(TMR3_IRQn); + + usb_register_handler(bl_handler); + usb_init(); + + ble_init(); + + wang_init(); + + while (1) { + WWDG_SetCounter(0x7F); + static int btldr_timer = 0; + if (button_pressed[1] && button_pressed[0]) { + btldr_timer++; + if (btldr_timer > 1000) asm volatile("j 0x00"); + } else { + btldr_timer = 0; + } + + static int cur_handler = 0; + if (cur_handler) { + cur_handler = menu_handler(); + if (!cur_handler) { + btn2_was_pressed = button_pressed[1]; + btn1_was_pressed = button_pressed[0]; + subframe = 99999; + } + } else + cur_handler = main_handler(); + + TMOS_SystemProcess(); + cdc_tick(); + hid_tick(); + DelayMs(1); + } +} diff --git a/src/menu.c b/src/menu.c new file mode 100644 index 0000000..44e57bd --- /dev/null +++ b/src/menu.c @@ -0,0 +1,145 @@ +#include +#include + +#include "CH58x_common.h" +#include "CH58x_sys.h" +#include "CH58xBLE_LIB.h" +#include "usb/core.h" +#include "led.h" +#include "ble.h" +#include "button.h" + +#include "img/menu.xbm" + +static void boop() +{ + WWDG_ResetCfg(DISABLE); + PFIC_DisableIRQ(TMR0_IRQn); + PFIC_DisableIRQ(TMR3_IRQn); + + // Stop wasting energy + GPIOA_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_Floating); + GPIOB_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_Floating); + + GPIOA_ModeCfg(GPIO_Pin_1, GPIO_ModeIN_PD); + while (GPIOA_ReadPortPin(GPIO_Pin_1)) DelayMs(1); + + DelayMs(100); + // 0 to 1 (pulldown, so implies being _pressed_) + // however, this doesn't work? + GPIOA_ITModeCfg(GPIO_Pin_1, GPIO_ITMode_HighLevel); + PFIC_EnableIRQ(GPIO_A_IRQn); + // Wait for shit to settle + while (GPIOA_ReadPortPin(GPIO_Pin_1)) DelayMs(1); + PWR_PeriphWakeUpCfg(ENABLE, RB_SLP_GPIO_WAKE, Long_Delay); + + /* Good bye */ + LowPower_Shutdown(0); +} + +static void render_xbm(unsigned char *bits, struct row_buf *b) { + uint16_t fb[45] = {0}; + int index = 0; + for (int j = 0; j < 11; j++) { + index = j * 48; + for (int i = 0; i < 44; i++) { + int bit = bits[index / 8] & (1 << (index % 8)); + if (!bit) fb[i] |= (1 << j); + index++; + } + } + + display_make_buf_all(fb, b); +} + +int menu_index = 0; + +static int btn0_pressed = 0; +static int btn1_pressed = 0; + +void menu_render(void) { + char buffer[(48 * 11) / 8]; + memcpy(buffer, menu_bits, sizeof(buffer)); + int invert_index = 1 + menu_index * 13; + for (int i = invert_index; i < (invert_index + 9); i++) { + for (int j = 1; j < 10; j++) + buffer[(i / 8) + (j * 48 / 8)] ^= (1 << (i % 8)); + } + + int batt_percent = ((get_battery_percentage() + 7) * 10) / 100; + int charge = is_charging() || is_plugged(); + for (int i = 39; i < 43; i++) { + for (int j = 0; j < 10; j++) { + int index = 10 - j; + if (j <= batt_percent) { + buffer[(i / 8) + (index * 48 / 8)] &= ~(1 << (i % 8)); + } else if (charge && (j - 2) <= batt_percent) { + if (i % 2 == j % 2) + buffer[(i / 8) + (index * 48 / 8)] &= ~(1 << (i % 8)); + } + } + } + + if (!ble_on) { + #define PIX(x, y) buffer[((x) / 8) + ((y) * 48 / 8)] |= (1 << ((x) % 8)) + for (int i = 0; i < 11; i += 2) { + PIX(13 + i, 0); + PIX(13 + i, 10); + PIX(13, i); + PIX(23, i); + } + } + + render_xbm(buffer, display_screen); + display_flip(); +} + +void menu_switch(void) { + btn0_pressed = button_pressed[0]; + btn1_pressed = button_pressed[1]; + menu_index = 0; + + menu_render(); +} + +int menu_handler(void) { + static int btldr_timer = 0; + static int render_ticks = 0; + render_ticks++; + + if (button_pressed[1] && button_pressed[0]) { + btldr_timer++; + if (btldr_timer > 2000) asm volatile("j 0x00"); + } else { + btldr_timer = 0; + } + + if (button_pressed[0] && !btn0_pressed && !button_pressed[1]) { + menu_index = (menu_index + 1) % 3; + menu_render(); + render_ticks = 0; + } + + if (button_pressed[1] && !btn1_pressed && !button_pressed[0]) { + if (menu_index == 0) { + return 0; + } else if (menu_index == 1) { + ble_toggle(); + menu_render(); + render_ticks = 0; + } else if (menu_index == 2) { + boop(); + } + } + + if (render_ticks > 125) { + render_ticks = 0; + menu_render(); + } + + btn0_pressed = button_pressed[0]; + btn1_pressed = button_pressed[1]; + + return 1; +} + diff --git a/src/menu.h b/src/menu.h new file mode 100644 index 0000000..57f21ae --- /dev/null +++ b/src/menu.h @@ -0,0 +1,4 @@ +#pragma once + +void menu_switch(void); +int menu_handler(void); diff --git a/src/usb/core.c b/src/usb/core.c new file mode 100644 index 0000000..2ca6fe1 --- /dev/null +++ b/src/usb/core.c @@ -0,0 +1,232 @@ +#include +#include "core.h" +#include "CH58x_common.h" + +static volatile uint8_t *ep_t_len_regs[] = { + &R8_UEP0_T_LEN, + &R8_UEP1_T_LEN, + &R8_UEP2_T_LEN, + &R8_UEP3_T_LEN, + &R8_UEP4_T_LEN, + &R8_UEP5_T_LEN, + &R8_UEP6_T_LEN, + &R8_UEP7_T_LEN, +}; + +static volatile uint8_t *ep_ctrl_regs[] = { + &R8_UEP0_CTRL, + &R8_UEP1_CTRL, + &R8_UEP2_CTRL, + &R8_UEP3_CTRL, + &R8_UEP4_CTRL, + &R8_UEP5_CTRL, + &R8_UEP6_CTRL, + &R8_UEP7_CTRL, +}; + +static uint8_t recvlens[8]; +uint8_t bConfigurationValue = 0; + +// The hardware requires EP0 and EP4 to share a contiguous DMA buffer. +// Fuck if I know why. +__attribute__((aligned(4))) volatile uint8_t ep04_buf[64 + 64 + 64]; + +__attribute__((aligned(4))) volatile uint8_t epbuf[(8 - 2) * 2 * 64]; + +volatile uint8_t *buf_for_ep(uint8_t ep, int dir_in) { + if (ep == 0) return ep04_buf; + if (ep == 4) return ep04_buf + (dir_in ? 128 : 64); + + // EP0 and EP4 aren't in here. so skip them. + ep -= 1; + if (ep > 3) ep -= 1; + return epbuf + (ep * 128 + (dir_in ? 64 : 0)); +} + +// Configure the response to set for the next token for the endpoint. +// The interrupt handler will reconfigure this to return NAK whenever +// the next token has been received. +enum usb_endpoint_state set_endpoint_state(uint8_t endpoint, enum usb_endpoint_state state) { + int dir_in = endpoint & 0x80; // 0 -> OUT endpoint, 1 -> IN endpoint + int ep_num = endpoint & 0x7F; + + if (dir_in) { + uint8_t prev_state = *ep_ctrl_regs[ep_num] & MASK_UEP_T_RES; + *ep_ctrl_regs[ep_num] = (*ep_ctrl_regs[ep_num] & ~MASK_UEP_T_RES) | (state & 0x03); + return prev_state; + } else { + uint8_t prev_state = (*ep_ctrl_regs[ep_num] & MASK_UEP_R_RES) >> 2; + *ep_ctrl_regs[ep_num] = (*ep_ctrl_regs[ep_num] & ~MASK_UEP_R_RES) | ((state & 0x03) << 2); + return prev_state; + } +} + + +// Toggle the DATA0/DATA1 toggle bit, indicating which of +// DATA0 and DATA1 to use fo rthe next receiving or sending of data. +// These bits are only toggled once data has been successfully transferred; +// a la USB_EP_STATE_ACK. +// +// The SETUP control transfers on endoint 0 _always_ use DATA0. +static void flip_endpoint_data_toggle(uint8_t endpoint) { + int dir_in = endpoint & 0x80; + int ep_num = endpoint & 0x7F; + + if (dir_in) { + *ep_ctrl_regs[ep_num] ^= RB_UEP_T_TOG; + } else { + *ep_ctrl_regs[ep_num] ^= RB_UEP_R_TOG; + } +} + +static void handle_bus_reset() { + R8_USB_INT_FG = RB_UIF_BUS_RST; + R8_USB_DEV_AD = 0; + bConfigurationValue = 0; +} + +static void handle_suspend() { + R8_USB_INT_FG = RB_UIF_SUSPEND; + + // TODO +} + +static void handle_sof() { + R8_USB_INT_FG = RB_UIF_HST_SOF; +} + + +__HIGH_CODE +static void handle_transfer(uint8_t ep_num, int is_in) { + uint8_t endpoint = ep_num | (is_in ? 0x80 : 0); + + // We may have received a transfer. + // If we expected this transfer, toggle the DATA0/DATA1 bits. + enum usb_endpoint_state prev_state = set_endpoint_state(endpoint, USB_EP_STATE_NAK); + if (prev_state == USB_EP_STATE_ACK) { + flip_endpoint_data_toggle(endpoint); + } + + if (ep_num == 0) { + handle_ctrl_transfer(is_in); + return; + } + + if (!is_in) { + recvlens[endpoint] = R8_USB_RX_LEN; + } +} + +void usb_init() { + + R8_USB_CTRL = 0x00; + + R8_UEP4_1_MOD = RB_UEP4_RX_EN | RB_UEP4_TX_EN | + RB_UEP1_RX_EN | RB_UEP1_TX_EN; + R8_UEP2_3_MOD = RB_UEP2_RX_EN | RB_UEP2_TX_EN | + RB_UEP3_RX_EN | RB_UEP3_TX_EN; + R8_UEP567_MOD = RB_UEP7_RX_EN | RB_UEP7_TX_EN | + RB_UEP6_RX_EN | RB_UEP6_TX_EN | + RB_UEP5_RX_EN | RB_UEP5_TX_EN; + + R16_PIN_ANALOG_IE |= RB_PIN_USB_IE | RB_PIN_USB_DP_PU; + R8_UDEV_CTRL = RB_UD_PD_DIS | RB_UD_PORT_EN; + + R8_USB_DEV_AD = 0x00; + R8_USB_INT_FG = 0xFF; + + R16_UEP0_DMA = ep04_buf; + R16_UEP1_DMA = epbuf + 0 * 128; + R16_UEP2_DMA = epbuf + 1 * 128; + R16_UEP3_DMA = epbuf + 2 * 128; + R16_UEP5_DMA = epbuf + 3 * 128; + R16_UEP6_DMA = epbuf + 4 * 128; + R16_UEP7_DMA = epbuf + 5 * 128; + + for (int i = 0; i < 8; i++) { + set_endpoint_state(i, USB_EP_STATE_NAK); + set_endpoint_state(i | 0x80, USB_EP_STATE_NAK); + } + + R8_USB_CTRL = RB_UC_DEV_PU_EN | RB_UC_INT_BUSY | RB_UC_DMA_EN; + R8_USB_INT_EN = RB_UIE_SUSPEND | RB_UIE_BUS_RST | RB_UIE_TRANSFER; + + PFIC_EnableIRQ(USB_IRQn); +} + +__INTERRUPT +__HIGH_CODE +void USB_IRQHandler(void) { + uint8_t interrupt = R8_USB_INT_FG; + + if (interrupt & RB_UIF_BUS_RST) { + handle_bus_reset(); + } + + if (interrupt & RB_UIF_SUSPEND) { + handle_suspend(); + } + + if (interrupt & RB_UIF_TRANSFER) { + uint8_t status = R8_USB_INT_ST; + uint8_t ep_num = status & MASK_UIS_ENDP; + uint8_t token = ((status & MASK_UIS_TOKEN) >> 4) & 0x03; + + if (token == 0x00) { // OUT + if (status & RB_UIS_TOG_OK) handle_transfer(ep_num, 0); + R8_USB_INT_FG = RB_UIF_TRANSFER; + } else if (token == 0x01) { // SOF + handle_sof(); + R8_USB_INT_FG = RB_UIF_TRANSFER; + } else if (token == 0x02) { // IN + handle_transfer(ep_num, 1); + R8_USB_INT_FG = RB_UIF_TRANSFER; + } + if ((status & RB_UIS_SETUP_ACT)) { + handle_setup_request(); + R8_USB_INT_FG = RB_UIF_TRANSFER; + } + } +} + +// Receive up to buflen bytes from selected OUT endpoint. +// If data is not yet available, returns -1. +int16_t usb_recv(uint8_t endpoint, void *buffer, size_t buflen) { + if (recvlens[endpoint] == 0xFF) { + set_endpoint_state(endpoint, USB_EP_STATE_ACK); + return -1; + } + + if (recvlens[endpoint] < buflen) { + buflen = recvlens[endpoint]; + } + + recvlens[endpoint] = 0xFF; + + memcpy(buffer, buf_for_ep(endpoint, 0), buflen); + return buflen; +} + +// Sends up to buflen bytes to selected IN endpoint. +// If the buffer is still busy, returns -1. +int16_t usb_xmit(uint8_t endpoint, void *buffer, size_t buflen) { + if ((*ep_ctrl_regs[endpoint] & MASK_UEP_T_RES) != USB_EP_STATE_NAK) { + return -1; + } + + if (buflen > 64) buflen = 64; + + memcpy(buf_for_ep(endpoint, 1), buffer, buflen); + *ep_t_len_regs[endpoint] = buflen; + set_endpoint_state(endpoint | 0x80, USB_EP_STATE_ACK); + + return buflen; +} + +int usb_can_xmit(uint8_t endpoint) { + return (*ep_ctrl_regs[endpoint] & MASK_UEP_T_RES) == USB_EP_STATE_NAK; +} + +int usb_connected(void) { + return !!(R8_USB_MIS_ST & RB_UMS_SUSPEND); +} diff --git a/src/usb/core.h b/src/usb/core.h new file mode 100644 index 0000000..9cd761b --- /dev/null +++ b/src/usb/core.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +enum usb_endpoint_state { + USB_EP_STATE_ACK = 0, + USB_EP_STATE_TIMEOUT = 1, + USB_EP_STATE_NAK = 2, + USB_EP_STATE_STALL = 3, +}; + +struct __attribute__((packed)) usb_control_request { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +}; + +extern struct usb_control_request usb_control_request; +extern void *control_transfer_buf; +extern uint16_t control_transfer_len; +extern uint8_t bConfigurationValue; + +extern volatile uint8_t ep04_buf[64 + 64 + 64]; +extern volatile uint8_t epbuf[(8 - 2) * 2 * 64]; + +enum usb_control_resp { + USB_CONTROL_RESP_PASS, + USB_CONTROL_RESP_ACK, + USB_CONTROL_RESP_STALL, +}; + +enum usb_control_state { + USB_CONTROL_SETUP, + USB_CONTROL_DATA_SENT, + USB_CONTROL_FINISHED, +}; + +typedef enum usb_control_resp (*usb_control_request_handler)(enum usb_control_state); +void usb_register_handler(usb_control_request_handler); + +enum usb_control_request_type { + USB_CONTROL_REQUEST_TYPE_OUT, + USB_CONTROL_REQUEST_TYPE_IN, + USB_CONTROL_REQUEST_TYPE_NO_DATA, +}; + +void ctrl_respond_out(void *buf, uint16_t len); +void handle_ctrl_transfer(int is_in); + +int16_t usb_recv(uint8_t endpoint, void *buffer, size_t buflen); +int16_t usb_xmit(uint8_t endpoint, void *buffer, size_t buflen); +int usb_can_xmit(uint8_t ep); + +void usb_init(); +void handle_setup_request(); +enum usb_endpoint_state set_endpoint_state(uint8_t endpoint, enum usb_endpoint_state state); + +int usb_connected(void); diff --git a/src/usb/ctrl.c b/src/usb/ctrl.c new file mode 100644 index 0000000..cb69ea5 --- /dev/null +++ b/src/usb/ctrl.c @@ -0,0 +1,362 @@ +#include +#include "core.h" +#include "CH58x_common.h" + +struct usb_control_request usb_control_request; + +// These track the currently ongoing control transfer. +void *control_transfer_buf; +uint16_t control_transfer_len; + +void ctrl_respond_out(void *buf, uint16_t len) { + control_transfer_buf = buf; + if (len > usb_control_request.wLength) len = usb_control_request.wLength; + control_transfer_len = len; +} + +__HIGH_CODE +static enum usb_control_request_type ctrlreq_type() { + if (!usb_control_request.wLength) { + return USB_CONTROL_REQUEST_TYPE_NO_DATA; + } + + return usb_control_request.bmRequestType & 0x80 ? USB_CONTROL_REQUEST_TYPE_IN : USB_CONTROL_REQUEST_TYPE_OUT; +} + +uint8_t device_descriptor[18] = { + 0x12, + 0x01, + 0x00, 0x02, + 0x00, + 0x00, + 0x00, + 0x40, + 0x16, 0x04, + 0x20, 0x50, + 0x20, 0x04, + 0x00, + 0x00, + 0x00, + 0x01, +}; + +uint8_t config_descriptor[0x6b] = { + 0x09, // bLength + 0x02, // bDescriptorType + 0x6b, 0x00, // wTotalLength + 0x03, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes + 0x64, // bMaxPower + + 0x09, // bLength + 0x04, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0x03, // bInterfaceClass (HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface + + // HID descriptor + 0x09, + 0x21, + 0x11, 0x01, + 0x00, + 0x01, + 0x22, + 0x18, 0x00, + + 0x07, + 0x05, + 0x82, + 0x03, + 0x10, 0x00, + 0x01, + + 0x07, + 0x05, + 0x02, + 0x03, + 0x10, 0x00, + 0x01, + + + 0x08, // bLength + 0x0b, // bDescriptorType ~ INTERFACE ASSOCIATION DESCRIPTOR + 0x01, // bFirstInterface + 0x02, // bInterfaceCount + 0x02, // bFunctionClass 0x02 + 0x02, // bFunctionSubclass 0x02 + 0x01, // bFunctionProtocol 0x01 + 0x00, // iFunction + + 0x09, // bLength + 0x04, // bDescriptorType + 0x01, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x01, // bNumEndpoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0x00, // iInterface + + 0x05, // bLength + 0x24, // bDescriptorType + 0x00, // bDescriptorSubtype + 0x10, 0x01, // bcdCDC + + 0x05, // bLength + 0x24, // bDescriptorType + 0x01, // bDescriptorSubtype + 0x00, // bmCapabilities + 0x01, // bDataInterface + + // ACM descripotor + 0x04, // bLength + 0x24, // bDescriptorType + 0x02, // bDescriptorSubtype + 0x00, // bmCapabilities + + // ACM union descriptor + 0x05, // bLength + 0x24, // bDescriptorType + 0x06, // bDescriptorSubtype + 0x00, // bMasterInterface + 0x01, // bSlaveInterface0 + + 0x07, // bLength + 0x05, // bDescriptorType + 0x88, // bEndpointAddress + 0x03, // bmAttributes + 0x10, 0x00, // wMaxPacketSize + 0x01, // bInterval + + 0x09, // bLength + 0x04, // bDescriptorType + 0x02, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0x0a, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface + + 0x07, // bLength + 0x05, // bDescriptorType + 0x81, // bEndpointAddress + 0x02, // bmAttributes + 0x20, 0x00, // wMaxPacketSize + 0x01, // bInterval + + 0x07, // bLength + 0x05, // bDescriptorType + 0x01, // bEndpointAddress + 0x02, // bmAttributes + 0x20, 0x00, // wMaxPacketSize + 0x01, // bInterval +}; + +uint8_t report_descriptor[24] = { + 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) + 0x09, 0x01, // Usage (0x01) + 0xA1, 0x01, // Collection (Application) + 0x09, 0x02, // Usage (0x02) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0xFF, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x40, // Report Count (64) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x09, 0x03, // Usage (0x03) + 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0xC0 // End Collection +}; + +__HIGH_CODE +static enum usb_control_resp handle_hid_request(enum usb_control_state state) { + if ((usb_control_request.bmRequestType & 0x7F) != 0x21 && (usb_control_request.bmRequestType & 0x7f) != 1) return USB_CONTROL_RESP_PASS; + if (usb_control_request.wIndex != 0) return USB_CONTROL_RESP_PASS; + + if (usb_control_request.bmRequestType == 0x81 && usb_control_request.bRequest == 0x06 && usb_control_request.wValue == 0x2200) { + if (state == USB_CONTROL_SETUP) ctrl_respond_out(report_descriptor, 24); + return USB_CONTROL_RESP_ACK; + } + + return USB_CONTROL_RESP_STALL; +} + +__HIGH_CODE +static enum usb_control_resp handle_main_request(enum usb_control_state state) { + if (usb_control_request.bmRequestType & 0x7F != 0x00) return USB_CONTROL_RESP_PASS; + + switch (usb_control_request.bRequest) { + case 0: // GET_STATUS + if (state == USB_CONTROL_SETUP) { + static uint16_t status_val = 0; + ctrl_respond_out((void *)&status_val, 2); + } + + return USB_CONTROL_RESP_ACK; + case 1: // CLEAR_FEATURE + case 3: // SET_FEATURE + return USB_CONTROL_RESP_STALL; + case 5: // SET_ADDRESS + if (state == USB_CONTROL_FINISHED) { + R8_USB_DEV_AD = (uint8_t) usb_control_request.wValue; + } + + return USB_CONTROL_RESP_ACK; + case 6: // GET_DESCRIPTOR + if ((usb_control_request.wValue & 0xFF00) == 0x0100) { + if (state == USB_CONTROL_SETUP) ctrl_respond_out(device_descriptor, sizeof(device_descriptor)); + } else if ((usb_control_request.wValue & 0xFF00) == 0x0200) { + if (state == USB_CONTROL_SETUP) ctrl_respond_out(config_descriptor, sizeof(config_descriptor)); + } else { + return USB_CONTROL_RESP_STALL; + } + + return USB_CONTROL_RESP_ACK; + case 7: // SET_DESCRIPTOR + return USB_CONTROL_RESP_STALL; + case 8: // GET_CONFIGURATION + if (state == USB_CONTROL_SETUP) { + ctrl_respond_out(&bConfigurationValue, 1); + } + + return USB_CONTROL_RESP_STALL; + case 9: // SET_CONFIGURATION + if (usb_control_request.wValue > 1 || usb_control_request.wLength || usb_control_request.wIndex) { + return USB_CONTROL_RESP_STALL; + } + + return USB_CONTROL_RESP_ACK; + + case 10: // GET_INTERFACE + case 11: // SET_INTERFACE + case 12: // SYNCH_FRAME + return USB_CONTROL_RESP_STALL; + } + + return USB_CONTROL_RESP_STALL; +} + +static usb_control_request_handler handlers[16] = {0}; +static int handlers_index = 0; + +void usb_register_handler(usb_control_request_handler handler) { + handlers[handlers_index++] = handler; +} + +__HIGH_CODE +static enum usb_control_resp call_handler(enum usb_control_state state) { + enum usb_control_resp r = handle_main_request(state); + if (r != USB_CONTROL_RESP_PASS) return r; + + r = handle_hid_request(state); + if (r != USB_CONTROL_RESP_PASS) return r; + + for (int i = 0; i < handlers_index; i++) { + if (!handlers[i]) continue; + enum usb_control_resp resp = handlers[i](state); + if (resp != USB_CONTROL_RESP_PASS) return resp; + } + + return USB_CONTROL_RESP_STALL; +} + +__HIGH_CODE +void handle_ctrl_transfer(int is_in) { + if (!control_transfer_buf) { + call_handler(USB_CONTROL_FINISHED); + if (is_in) { + R8_UEP0_CTRL = RB_UEP_T_TOG | RB_UEP_R_TOG | UEP_R_RES_ACK | UEP_T_RES_STALL; + return; + } + R8_UEP0_CTRL = RB_UEP_T_TOG | RB_UEP_R_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL; + return; + } + + + if (is_in) { + // Account for the bytes having been sent. + if (control_transfer_len >= 64) { + control_transfer_len -= 64; + control_transfer_buf += 64; + } else + control_transfer_len = 0; + + // If we have more data to transfer, set up the next + // DMA. + + if (control_transfer_len > 0) { + memcpy(ep04_buf, control_transfer_buf, control_transfer_len > 64 ? 64 : control_transfer_len); + R8_UEP0_T_LEN = control_transfer_len > 64 ? 64 : control_transfer_len; + set_endpoint_state(0x80, USB_EP_STATE_ACK); + } else { + enum usb_control_resp result = call_handler(USB_CONTROL_DATA_SENT); + control_transfer_buf = 0; + set_endpoint_state(0x00, result == USB_CONTROL_RESP_ACK ? USB_EP_STATE_ACK : USB_EP_STATE_STALL); + } + } else { + uint8_t ack_bytes = R8_USB_RX_LEN; + if (ack_bytes > control_transfer_len) { + ack_bytes = control_transfer_len; + } + + control_transfer_len -= ack_bytes; + memcpy(control_transfer_buf, ep04_buf, ack_bytes); + control_transfer_buf += ack_bytes; + + if (control_transfer_len > 0) { + // We have more data to receive. so re-ready the endpoint. + set_endpoint_state(0x00, USB_EP_STATE_ACK); + } else { + enum usb_control_resp result = call_handler(USB_CONTROL_DATA_SENT); + control_transfer_buf = 0; + set_endpoint_state(0x80, result == USB_CONTROL_RESP_ACK ? USB_EP_STATE_ACK : USB_EP_STATE_STALL); + } + } +} + +__HIGH_CODE +void handle_setup_request() { + // Copy the setup request from the EP0 buffer. + memcpy(&usb_control_request, ep04_buf, 8); + + // We don't expect the previous request to be driven anymore. + // Reset the control transfer. + control_transfer_buf = 0; + control_transfer_len = 0; + + // The next requests (and responses) will start with DATA1. + R8_UEP0_CTRL = RB_UEP_T_TOG | RB_UEP_R_TOG | UEP_R_RES_NAK | UEP_T_RES_NAK; + + if (call_handler(USB_CONTROL_SETUP) != USB_CONTROL_RESP_ACK) { + control_transfer_buf = 0; + control_transfer_len = 0; + + // We need to stall this request. + // This is done by stalling either the first data stage packet, + // or the status stage packet. + + // In an OUT type control request, the next request will be an OUT. + // In other cases, it will always be IN. + if (ctrlreq_type() != USB_CONTROL_REQUEST_TYPE_OUT) { + set_endpoint_state(0x80, USB_EP_STATE_STALL); + } else { + set_endpoint_state(0x00, USB_EP_STATE_STALL); + } + } else { + if (ctrlreq_type() != USB_CONTROL_REQUEST_TYPE_OUT) { + int mlen = control_transfer_len; + if (mlen > 64) mlen = 64; + memcpy(ep04_buf, control_transfer_buf, mlen); + R8_UEP0_T_LEN = mlen; + set_endpoint_state(0x80, USB_EP_STATE_ACK); + } else { + set_endpoint_state(0x00, USB_EP_STATE_ACK); + } + } +} diff --git a/src/wang.c b/src/wang.c new file mode 100644 index 0000000..f314ee1 --- /dev/null +++ b/src/wang.c @@ -0,0 +1,184 @@ +#include +#include +#include "CH58x_common.h" +#include "CH58x_sys.h" +#include "CH58xBLE_LIB.h" +#include "ISP583.h" +#include "led.h" +#include "wang.h" +#include "usb/core.h" + +enum state { + RECV_HEADER, + RECV_WIDTHS, + RECV_TIME, + RECV_BLANK, + RECV_DATA, +}; + +__attribute__((aligned(4))) +uint8_t wang_buf[16]; +int wang_buf_pos = 0; +int expected_data_chunks = 0; +int data_chunk_index = 0; +struct wang_header cur_header; + +int wang_rx_inner(const uint8_t *data, int bytes) { + // First try to fill the wang_buf. + int max_bytes = 16 - wang_buf_pos; + int to_copy = bytes; + if (to_copy > max_bytes) to_copy = max_bytes; + + memcpy(wang_buf + wang_buf_pos, data, to_copy); + wang_buf_pos += to_copy; + + if (wang_buf_pos < 16) return to_copy; + + static enum state cur_state = RECV_HEADER; + if (memcmp(wang_buf, "wang\0\0", 6) == 0) { + // We assume "wang\0\0" never happens in real data. + // This is technically a bad assumption, but also, + // who gives a shit. + cur_state = RECV_HEADER; + } else if (cur_state == RECV_HEADER) { + // Assume all our data is properly 16-byte aligned always. + // This is a really sucky assumption, though. + wang_buf_pos = 0; + return to_copy; + } + + switch (cur_state) { + case RECV_HEADER: + memcpy(cur_header.magic, wang_buf, 16); + cur_state = RECV_WIDTHS; + break; + case RECV_WIDTHS: + expected_data_chunks = 0; + for (int i = 0; i < 8; i++) { + cur_header.widths[i] = ((uint16_t)wang_buf[i * 2] << 8) | ((uint16_t)wang_buf[i * 2 + 1]); + expected_data_chunks += cur_header.widths[i] * 11; + } + data_chunk_index = 0; + cur_state = RECV_TIME; + break; + case RECV_TIME: + memcpy(cur_header.padding, wang_buf, 16); + cur_state = RECV_BLANK; + break; + case RECV_BLANK: + memcpy(cur_header.blank, wang_buf, 16); + cur_state = RECV_DATA; + // We are ready to receive the data. This is where things get a bit wonky. + // _erase_ all of flash while we work on this + int to_erase = sizeof(struct wang_header) + expected_data_chunks; + while (to_erase % 16) to_erase++; + if (to_erase < EEPROM_MIN_ER_SIZE) + to_erase = EEPROM_MIN_ER_SIZE; + EEPROM_ERASE(0, to_erase); + break; + case RECV_DATA: + EEPROM_WRITE(sizeof(struct wang_header) + 16 * data_chunk_index, wang_buf, 16); + data_chunk_index++; + if ((data_chunk_index * 16) > expected_data_chunks) { + EEPROM_WRITE(0, (uint8_t *)&cur_header, sizeof(struct wang_header)); + wang_init(); + cur_state = RECV_HEADER; + } + break; + } + + wang_buf_pos = 0; + + return to_copy; +} + +void wang_rx(const uint8_t *data, int length) { + while (length) { + int len = wang_rx_inner(data, length); + data += len; + length -= len; + } +} + +__attribute__((aligned(4))) +struct wang_header flash_header; +int flash_header_valid = 0; +void wang_init(void) { + EEPROM_READ(0, &flash_header, sizeof(struct wang_header)); + if (memcmp(flash_header.magic, "wang\0\0", 6) != 0) { + flash_header_valid = 0; + return; + } + + flash_header_valid = 1; +} + +void wang_render(int index, struct row_buf *output) { + uint16_t width = flash_header.widths[index]; + if (width == 0) return; + + int start_i = sizeof(struct wang_header); + for (int i = 0; i < index; i++) { + start_i += flash_header.widths[i] * 11; + } + + // We oversize this fb to deal with whatever bullshit. + if (width > 6) width = 6; + uint16_t fb[48] = {0}; + + for (int j = 0; j < width; j++) { + // We read a single 11-pixel tall row, and byte-swap it into this screen. + __attribute__((aligned(4))) + uint8_t chunk[11]; + EEPROM_READ(start_i + j * 11, chunk, 11); + + for (int y = 0; y < 11; y++) { + for (int x = 0; x < 8; x++) { + // read most-significant-bit first + if (chunk[y] & (1 << (7 - x))) + fb[j * 8 + x] |= (1 << y); + } + } + } + + display_make_buf_all(fb, output); +} + +void wang_read(int index, int offset, int width, uint16_t *out) { + uint16_t iwidth = flash_header.widths[index]; + if (iwidth == 0) return; + + int start_i = sizeof(struct wang_header); + for (int i = 0; i < index; i++) { + start_i += flash_header.widths[i] * 11; + } + + int start_page = offset / 8; + int end_page = ((offset + width - 1) / 8); + if (end_page >= iwidth) end_page = iwidth - 1; + + int start_x_global = offset; + int end_x_global = offset + width; + uint16_t *out_frobbed = out - start_x_global; + + for (int i = start_page; i <= end_page; i++) { + __attribute__((aligned(4))) + uint8_t chunk[11]; + EEPROM_READ(start_i + i * 11, chunk, 11); + + // Recalculate the start and end x relative to + // our position + int start_x = start_x_global - i * 8; + int end_x = end_x_global - i * 8; + if (start_x < 0) start_x = 0; + if (end_x > 7) end_x = 7; + + uint16_t *out_page = out_frobbed + i * 8; + for (int y = 0; y < 11; y++) { + for (int x = start_x; x <= end_x; x++) { + if (chunk[y] & (1 << (7 - x))) + out_page[x] |= (1 << y); + } + } + } +} diff --git a/src/wang.h b/src/wang.h new file mode 100644 index 0000000..fba6e93 --- /dev/null +++ b/src/wang.h @@ -0,0 +1,30 @@ +#pragma once + +struct wang_header { + // First 16-byte chunk + uint8_t magic[6]; // "wang\0\0" + uint8_t flash_map; + uint8_t marquee_map; + uint8_t speed_and_mode[8]; + + // Second 16-byte chunk + uint16_t widths[8]; + + // Third 16-byte chunk + uint8_t padding[6]; + uint8_t time[6]; + uint8_t padding2[4]; + + // Fourth 16-byte chunk, empty + uint8_t blank[16]; +}; + +// Receive some data in wang format +void wang_rx(const uint8_t *, int); + +extern struct wang_header flash_header; +extern int flash_header_valid; +void wang_init(void); +void wang_render(int index, struct row_buf *output); + +void wang_read(int index, int offset, int width, uint16_t *buf);