diff --git a/bacnet-stack/include/dlmstp.h b/bacnet-stack/include/dlmstp.h index 1ee84b66..6b4fc35d 100644 --- a/bacnet-stack/include/dlmstp.h +++ b/bacnet-stack/include/dlmstp.h @@ -113,6 +113,8 @@ extern "C" { bool dlmstp_sole_master( void); + bool dlmstp_send_pdu_queue_empty(void); + bool dlmstp_send_pdu_queue_full(void); #ifdef __cplusplus } diff --git a/bacnet-stack/ports/xplained/ASF/common/boards/board.h b/bacnet-stack/ports/xplained/ASF/common/boards/board.h new file mode 100644 index 00000000..391cc98a --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/boards/board.h @@ -0,0 +1,333 @@ +/** + * \file + * + * \brief Standard board header file. + * + * This file includes the appropriate board header file according to the + * defined board (parameter BOARD). + * + * Copyright (c) 2009-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#ifndef _BOARD_H_ +#define _BOARD_H_ + +/** + * \defgroup group_common_boards Generic board support + * + * The generic board support module includes board-specific definitions + * and function prototypes, such as the board initialization function. + * + * \{ + */ + +#include "compiler.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/*! \name Base Boards + */ +//! @{ +#define EVK1100 1 //!< AT32UC3A EVK1100 board. +#define EVK1101 2 //!< AT32UC3B EVK1101 board. +#define UC3C_EK 3 //!< AT32UC3C UC3C_EK board. +#define EVK1104 4 //!< AT32UC3A3 EVK1104 board. +#define EVK1105 5 //!< AT32UC3A EVK1105 board. +#define STK600_RCUC3L0 6 //!< STK600 RCUC3L0 board. +#define UC3L_EK 7 //!< AT32UC3L-EK board. +#define XPLAIN 8 //!< ATxmega128A1 Xplain board. +#define STK600_RC064X 10 //!< ATxmega256A3 STK600 board. +#define STK600_RC100X 11 //!< ATxmega128A1 STK600 board. +#define UC3_A3_XPLAINED 13 //!< ATUC3A3 UC3-A3 Xplained board. +#define UC3_L0_XPLAINED 15 //!< ATUC3L0 UC3-L0 Xplained board. +#define STK600_RCUC3D 16 //!< STK600 RCUC3D board. +#define STK600_RCUC3C0 17 //!< STK600 RCUC3C board. +#define XMEGA_B1_XPLAINED 18 //!< ATxmega128B1 Xplained board. +#define XMEGA_A1_XPLAINED 19 //!< ATxmega128A1 Xplain-A1 board. +#define STK600_RCUC3L4 21 //!< ATUCL4 STK600 board +#define UC3_L0_XPLAINED_BC 22 //!< ATUC3L0 UC3-L0 Xplained board controller board +#define MEGA1284P_XPLAINED_BC 23 //!< ATmega1284P-Xplained board controller board +#define STK600_RC044X 24 //!< STK600 with RC044X routing card board. +#define STK600_RCUC3B0 25 //!< STK600 RCUC3B0 board. +#define UC3_L0_QT600 26 //!< QT600 UC3L0 MCU board. +#define XMEGA_A3BU_XPLAINED 27 //!< ATxmega256A3BU Xplained board. +#define STK600_RC064X_LCDX 28 //!< XMEGAB3 STK600 RC064X LCDX board. +#define STK600_RC100X_LCDX 29 //!< XMEGAB1 STK600 RC100X LCDX board. +#define UC3B_BOARD_CONTROLLER 30 //!< AT32UC3B1 board controller for Atmel boards +#define RZ600 31 //!< AT32UC3A RZ600 MCU board +#define SAM3S_EK 32 //!< SAM3S-EK board. +#define SAM3U_EK 33 //!< SAM3U-EK board. +#define SAM3X_EK 34 //!< SAM3X-EK board. +#define SAM3N_EK 35 //!< SAM3N-EK board. +#define SAM3S_EK2 36 //!< SAM3S-EK2 board. +#define SAM4S_EK 37 //!< SAM4S-EK board. +#define STK600_RCUC3A0 38 //!< STK600 RCUC3A0 board. +#define STK600_MEGA 39 //!< STK600 MEGA board. +#define MEGA_1284P_XPLAINED 40 //!< ATmega1284P Xplained board. +#define SAM4S_XPLAINED 41 //!< SAM4S Xplained board. +#define ATXMEGA128A1_QT600 42 //!< QT600 ATXMEGA128A1 MCU board. +#define ARDUINO_DUE_X 43 //!< Arduino Due/X board. +#define STK600_RCUC3L3 44 //!< ATUCL3 STK600 board +#define SAM4L_EK 45 //!< SAM4L-EK board. +#define STK600_MEGA_RF 46 //!< STK600 MEGA RF EVK board. +#define XMEGA_C3_XPLAINED 47 //!< ATxmega384C3 Xplained board. +#define STK600_RC032X 48 //!< STK600 with RC032X routing card board. +#define SAM4S_EK2 49 //!< SAM4S-EK2 board. +#define XMEGA_E5_XPLAINED 50 //!< ATxmega32E5 Xplained board. +#define SAM4E_EK 51 //!< SAM4E-EK board. +#define ATMEGA256RFR2_XPLAINED_PRO 52 //!< ATmega256RFR2 Xplained Pro board. +#define SAM4S_XPLAINED_PRO 53 //!< SAM4S Xplained Pro board. +#define SAM4L_XPLAINED_PRO 54 //!< SAM4L Xplained Pro board. +#define ATMEGA256RFR2_ZIGBIT 55 //!< ATmega256RFR2 zigbit +#define XMEGA_RF233_ZIGBIT 56 //!< ATxmega256A3U with AT86RF233 zigbit +#define XMEGA_RF212B_ZIGBIT 57 //!< ATxmega256A3U with AT86RF212B zigbit +#define SAM4S_WPIR_RD 58 //!< SAM4S-WPIR-RD board. +#define SIMULATOR_XMEGA_A1 97 //!< Simulator for XMEGA A1 devices +#define AVR_SIMULATOR_UC3 98 //!< AVR SIMULATOR for AVR UC3 device family. +#define USER_BOARD 99 //!< User-reserved board (if any). +#define DUMMY_BOARD 100 //!< Dummy board to support board-independent applications (e.g. bootloader) +//! @} + +/*! \name Extension Boards + */ +//! @{ +#define EXT1102 1 //!< AT32UC3B EXT1102 board +#define MC300 2 //!< AT32UC3 MC300 board +#define SENSORS_XPLAINED_INERTIAL_1 3 //!< Xplained inertial sensor board 1 +#define SENSORS_XPLAINED_INERTIAL_2 4 //!< Xplained inertial sensor board 2 +#define SENSORS_XPLAINED_PRESSURE_1 5 //!< Xplained pressure sensor board +#define SENSORS_XPLAINED_LIGHTPROX_1 6 //!< Xplained light & proximity sensor board +#define SENSORS_XPLAINED_INERTIAL_A1 7 //!< Xplained inertial sensor board "A" +#define RZ600_AT86RF231 8 //!< AT86RF231 RF board in RZ600 +#define RZ600_AT86RF230B 9 //!< AT86RF230B RF board in RZ600 +#define RZ600_AT86RF212 10 //!< AT86RF212 RF board in RZ600 +#define SENSORS_XPLAINED_BREADBOARD 11 //!< Xplained sensor development breadboard +#define SECURITY_XPLAINED 12 //!< Xplained ATSHA204 board +#define USER_EXT_BOARD 99 //!< User-reserved extension board (if any). +//! @} + +#if BOARD == EVK1100 +# include "evk1100/evk1100.h" +#elif BOARD == EVK1101 +# include "evk1101/evk1101.h" +#elif BOARD == UC3C_EK +# include "uc3c_ek/uc3c_ek.h" +#elif BOARD == EVK1104 +# include "evk1104/evk1104.h" +#elif BOARD == EVK1105 +# include "evk1105/evk1105.h" +#elif BOARD == STK600_RCUC3L0 +# include "stk600/rcuc3l0/stk600_rcuc3l0.h" +#elif BOARD == UC3L_EK +# include "uc3l_ek/uc3l_ek.h" +#elif BOARD == STK600_RCUC3L4 +# include "stk600/rcuc3l4/stk600_rcuc3l4.h" +#elif BOARD == XPLAIN +# include "xplain/xplain.h" +#elif BOARD == STK600_MEGA + /*No header-file to include */ +#elif BOARD == STK600_MEGA_RF +# include "stk600.h" +#elif BOARD == ATMEGA256RFR2_XPLAINED_PRO +# include "atmega256rfr2_xplained_pro/atmega256rfr2_xplained_pro.h" +#elif BOARD == ATMEGA256RFR2_ZIGBIT +# include "atmega256rfr2_zigbit/atmega256rfr2_zigbit.h" +#elif BOARD == STK600_RC032X +# include "stk600/rc032x/stk600_rc032x.h" +#elif BOARD == STK600_RC044X +# include "stk600/rc044x/stk600_rc044x.h" +#elif BOARD == STK600_RC064X +# include "stk600/rc064x/stk600_rc064x.h" +#elif BOARD == STK600_RC100X +# include "stk600/rc100x/stk600_rc100x.h" +#elif BOARD == UC3_A3_XPLAINED +# include "uc3_a3_xplained/uc3_a3_xplained.h" +#elif BOARD == UC3_L0_XPLAINED +# include "uc3_l0_xplained/uc3_l0_xplained.h" +#elif BOARD == STK600_RCUC3B0 +# include "stk600/rcuc3b0/stk600_rcuc3b0.h" +#elif BOARD == STK600_RCUC3D +# include "stk600/rcuc3d/stk600_rcuc3d.h" +#elif BOARD == STK600_RCUC3C0 +# include "stk600/rcuc3c0/stk600_rcuc3c0.h" +#elif BOARD == XMEGA_B1_XPLAINED +# include "xmega_b1_xplained/xmega_b1_xplained.h" +#elif BOARD == STK600_RC064X_LCDX +# include "stk600/rc064x_lcdx/stk600_rc064x_lcdx.h" +#elif BOARD == STK600_RC100X_LCDX +# include "stk600/rc100x_lcdx/stk600_rc100x_lcdx.h" +#elif BOARD == XMEGA_A1_XPLAINED +# include "xmega_a1_xplained/xmega_a1_xplained.h" +#elif BOARD == UC3_L0_XPLAINED_BC +# include "uc3_l0_xplained_bc/uc3_l0_xplained_bc.h" +#elif BOARD == SAM3S_EK +# include "sam3s_ek/sam3s_ek.h" +# include "system_sam3s.h" +#elif BOARD == SAM3S_EK2 +# include "sam3s_ek2/sam3s_ek2.h" +# include "system_sam3sd8.h" +#elif BOARD == SAM3U_EK +# include "sam3u_ek/sam3u_ek.h" +# include "system_sam3u.h" +#elif BOARD == SAM3X_EK +# include "sam3x_ek/sam3x_ek.h" +# include "system_sam3x.h" +#elif BOARD == SAM3N_EK +# include "sam3n_ek/sam3n_ek.h" +# include "system_sam3n.h" +#elif BOARD == SAM4S_EK +# include "sam4s_ek/sam4s_ek.h" +# include "system_sam4s.h" +#elif BOARD == SAM4S_WPIR_RD +# include "sam4s_wpir_rd/sam4s_wpir_rd.h" +# include "system_sam4s.h" +#elif BOARD == SAM4S_XPLAINED +# include "sam4s_xplained/sam4s_xplained.h" +# include "system_sam4s.h" +#elif BOARD == SAM4S_EK2 +# include "sam4s_ek2/sam4s_ek2.h" +# include "system_sam4s.h" +#elif BOARD == MEGA_1284P_XPLAINED + /*No header-file to include */ +#elif BOARD == ARDUINO_DUE_X +# include "arduino_due_x/arduino_due_x.h" +# include "system_sam3x.h" +#elif BOARD == SAM4L_EK +# include "sam4l_ek/sam4l_ek.h" +#elif BOARD == SAM4E_EK +# include "sam4e_ek/sam4e_ek.h" +#elif BOARD == MEGA1284P_XPLAINED_BC +# include "mega1284p_xplained_bc/mega1284p_xplained_bc.h" +#elif BOARD == UC3_L0_QT600 +# include "uc3_l0_qt600/uc3_l0_qt600.h" +#elif BOARD == XMEGA_A3BU_XPLAINED +# include "xmega_a3bu_xplained/xmega_a3bu_xplained.h" +#elif BOARD == XMEGA_E5_XPLAINED +# include "xmega_e5_xplained/xmega_e5_xplained.h" +#elif BOARD == UC3B_BOARD_CONTROLLER +# include "uc3b_board_controller/uc3b_board_controller.h" +#elif BOARD == RZ600 +# include "rz600/rz600.h" +#elif BOARD == STK600_RCUC3A0 +# include "stk600/rcuc3a0/stk600_rcuc3a0.h" +#elif BOARD == ATXMEGA128A1_QT600 +# include "atxmega128a1_qt600/atxmega128a1_qt600.h" +#elif BOARD == STK600_RCUC3L3 +# include "stk600/rcuc3l3/stk600_rcuc3l3.h" +#elif BOARD == SAM4S_XPLAINED_PRO +# include "sam4s_xplained_pro/sam4s_xplained_pro.h" +#elif BOARD == SAM4L_XPLAINED_PRO +# include "sam4l_xplained_pro/sam4l_xplained_pro.h" +#elif BOARD == SIMULATOR_XMEGA_A1 +# include "simulator/xmega_a1/simulator_xmega_a1.h" +#elif BOARD == XMEGA_C3_XPLAINED +# include "xmega_c3_xplained/xmega_c3_xplained.h" +#elif BOARD == XMEGA_RF233_ZIGBIT +# include "xmega_rf233_zigbit/xmega_rf233_zigbit.h" +#elif BOARD == XMEGA_RF212B_ZIGBIT +# include "xmega_rf212b_zigbit/xmega_rf212b_zigbit.h" +#elif BOARD == AVR_SIMULATOR_UC3 +# include "avr_simulator_uc3/avr_simulator_uc3.h" +#elif BOARD == USER_BOARD + // User-reserved area: #include the header file of your board here (if any). +# include "user_board.h" +#elif BOARD == DUMMY_BOARD +# include "dummy/dummy_board.h" +#else +//# error No known Atmel board defined +#endif + +#if (defined EXT_BOARD) +# if EXT_BOARD == MC300 +# include "mc300/mc300.h" +# elif (EXT_BOARD == SENSORS_XPLAINED_INERTIAL_1) || \ + (EXT_BOARD == SENSORS_XPLAINED_INERTIAL_2) || \ + (EXT_BOARD == SENSORS_XPLAINED_INERTIAL_A1) || \ + (EXT_BOARD == SENSORS_XPLAINED_PRESSURE_1) || \ + (EXT_BOARD == SENSORS_XPLAINED_LIGHTPROX_1) || \ + (EXT_BOARD == SENSORS_XPLAINED_BREADBOARD) +# include "sensors_xplained/sensors_xplained.h" +# elif EXT_BOARD == RZ600_AT86RF231 +# include "at86rf231/at86rf231.h" +# elif EXT_BOARD == RZ600_AT86RF230B +# include "at86rf230b/at86rf230b.h" +# elif EXT_BOARD == RZ600_AT86RF212 +# include "at86rf212/at86rf212.h" +# elif EXT_BOARD == SECURITY_XPLAINED +# include "security_xplained.h" +# elif EXT_BOARD == USER_EXT_BOARD + // User-reserved area: #include the header file of your extension board here + // (if any). +# endif +#endif + + +#if (defined(__GNUC__) && defined(__AVR32__)) || (defined(__ICCAVR32__) || defined(__AAVR32__)) +#ifdef __AVR32_ABI_COMPILER__ // Automatically defined when compiling for AVR32, not when assembling. + +/*! \brief This function initializes the board target resources + * + * This function should be called to ensure proper initialization of the target + * board hardware connected to the part. + */ + extern void board_init (void); + +#endif // #ifdef __AVR32_ABI_COMPILER__ +#else +/*! \brief This function initializes the board target resources + * + * This function should be called to ensure proper initialization of the target + * board hardware connected to the part. + */ + extern void board_init (void); +#endif + + +#ifdef __cplusplus +} +#endif + +/** + * \} + */ + +#endif // _BOARD_H_ diff --git a/bacnet-stack/ports/xplained/ASF/common/drivers/nvm/common_nvm.h b/bacnet-stack/ports/xplained/ASF/common/drivers/nvm/common_nvm.h new file mode 100644 index 00000000..fde303bd --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/drivers/nvm/common_nvm.h @@ -0,0 +1,318 @@ +/** + * \file + * + * \brief Non volatile memories management + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#ifndef COMMON_NVM_H_INCLUDED +#define COMMON_NVM_H_INCLUDED + +#include "compiler.h" +#include "conf_board.h" +#include "parts.h" +#include "status_codes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(USE_EXTMEM) && defined(CONF_BOARD_AT45DBX) +#include "at45dbx.h" +#endif + +/* ! \name Non volatile memory types */ +/* ! @{ */ +typedef enum { + INT_FLASH /* !< Internal Flash */ + +#if (XMEGA || UC3 || SAM4S) + , INT_USERPAGE /* !< Userpage/User signature */ +#endif + +#if XMEGA + , INT_EEPROM /* !< Internal EEPROM */ +#endif + +#if defined(USE_EXTMEM) && defined(CONF_BOARD_AT45DBX) + , AT45DBX /* !< External AT45DBX dataflash */ +#endif +} mem_type_t; +/* ! @} */ + +#if SAM +# ifndef IFLASH_PAGE_SIZE +# define IFLASH_PAGE_SIZE IFLASH0_PAGE_SIZE +# endif + +# ifndef IFLASH_ADDR +# define IFLASH_ADDR IFLASH0_ADDR +# endif +#endif + +/** + * \defgroup nvm_group NVM service + * + * See \ref common_nvm_quickstart. + * + * This is the common API for non volatile memories. Additional features are + * available + * in the documentation of the specific modules. + * + */ + +/** + * \brief Initialize the non volatile memory specified. + * + * \param mem Type of non volatile memory to initialize + */ +status_code_t nvm_init(mem_type_t mem); + +/** + * \brief Read single byte of data. + * + * \param mem Type of non volatile memory to read + * \param address Address to read + * \param data Pointer to where to store the read data + */ +status_code_t nvm_read_char(mem_type_t mem, uint32_t address, uint8_t *data); + +/** + * \brief Write single byte of data. + * + * \param mem Type of non volatile memory to write + * \param address Address to write + * \param data Data to be written + */ +status_code_t nvm_write_char(mem_type_t mem, uint32_t address, uint8_t data); + +/** + * \brief Read \a len number of bytes from address \a address in non volatile + * memory \a mem and store it in the buffer \a buffer + * + * \param mem Type of non volatile memory to read + * \param address Address to read + * \param buffer Pointer to destination buffer + * \param len Number of bytes to read + */ +status_code_t nvm_read(mem_type_t mem, uint32_t address, void *buffer, + uint32_t len); + +/** + * \brief Write \a len number of bytes at address \a address in non volatile + * memory \a mem from the buffer \a buffer + * + * \param mem Type of non volatile memory to write + * \param address Address to write + * \param buffer Pointer to source buffer + * \param len Number of bytes to write + */ +status_code_t nvm_write(mem_type_t mem, uint32_t address, void *buffer, + uint32_t len); + +/** + * \brief Erase a page in the non volatile memory. + * + * \param mem Type of non volatile memory to erase + * \param page_number Page number to erase + */ +status_code_t nvm_page_erase(mem_type_t mem, uint32_t page_number); + +/** + * \brief Get the size of whole non volatile memory specified. + * + * \param mem Type of non volatile memory + * \param size Pointer to where to store the size + */ +status_code_t nvm_get_size(mem_type_t mem, uint32_t *size); + +/** + * \brief Get the size of a page in the non volatile memory specified. + * + * \param mem Type of non volatile memory + * \param size Pointer to where to store the size + */ +status_code_t nvm_get_page_size(mem_type_t mem, uint32_t *size); + +/** + * \brief Get the page number from the byte address \a address. + * + * \param mem Type of non volatile memory + * \param address Byte address of the non volatile memory + * \param num Pointer to where to store the page number + */ +status_code_t nvm_get_pagenumber(mem_type_t mem, uint32_t address, + uint32_t *num); + +/** + * \brief Enable security bit which blocks external read and write access + * to the device. + * + */ +status_code_t nvm_set_security_bit(void); + +/** + * \page common_nvm_quickstart Quick Start quide for common NVM driver + * + * This is the quick start quide for the \ref nvm_group "Common NVM driver", + * with step-by-step instructions on how to configure and use the driver in a + * selection of use cases. + * + * The use cases contain several code fragments. The code fragments in the + * steps for setup can be copied into a custom initialization function, while + * the steps for usage can be copied into, e.g., the main application function. + * + * \section nvm_basic_use_case Basic use case + * In this basic use case, NVM driver is configured for Internal Flash + * + * \section nvm_basic_use_case_setup Setup steps + * + * \subsection nvm_basic_use_case_setup_code Example code + * Add to you application C-file: + * \code + * if(nvm_init(INT_FLASH) == STATUS_OK) + * do_something(); + * \endcode + * + * \subsection nvm_basic_use_case_setup_flow Workflow + * -# Ensure that board_init() has configured selected I/Os for TWI function + * when using external AT45DBX dataflash + * -# Ensure that \ref conf_nvm.h is present for the driver. + * - \note This file is only for the driver and should not be included by the + * user. + * -# Call nvm_init \code nvm_init(INT_FLASH); \endcode + * and optionally check its return code + * + * \section nvm_basic_use_case_usage Usage steps + * \subsection nvm_basic_use_case_usage_code_writing Example code: Writing to + * non volatile memory + * Use in the application C-file: + * \code + * uint8_t buffer[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE}; + * + * if(nvm_write(INT_FLASH, test_address, (void *)buffer, sizeof(buffer)) == + * STATUS_OK) + * do_something(); + * \endcode + * + * \subsection nvm_basic_use_case_usage_flow Workflow + * -# Prepare the data you want to send to the non volatile memory + * \code uint8_t buffer[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE}; \endcode + * -# Call nvm_write \code nvm_write(INT_FLASH, test_address, (void *)buffer, + * sizeof(buffer)) \endcode + * and optionally check its return value for STATUS_OK. + * + * \subsection nvm_basic_use_case_usage_code_reading Example code: Reading from + * non volatile memory + * Use in application C-file: + * \code + * uint8_t data_read[8]; + * + * if(nvm_read(INT_FLASH, test_address, (void *)data_read, sizeof(data_read)) + * == STATUS_OK) { + * //Check read content + * if(data_read[0] == 0xAA) + * do_something(); + * } + * \endcode + * + * \subsection nvm_basic_use_case_usage_flow Workflow + * -# Prepare a data buffer that will read data from non volatile memory + * \code uint8_t data_read[8]; \endcode + * -# Call nvm_read \code nvm_read(INT_FLASH, test_address, (void *)data_read, + * sizeof(data_read)); \endcode + * and optionally check its return value for STATUS_OK. + * The data read from the non volatile memory are in data_read. + * + * \subsection nvm_basic_use_case_usage_code_erasing Example code: Erasing a + * page of non volatile memory + * Use in the application C-file: + * \code + * if(nvm_page_erase(INT_FLASH, test_page) == STATUS_OK) + * do_something(); + * \endcode + * + * \subsection nvm_basic_use_case_usage_flow Workflow + * -# Call nvm_page_erase \code nvm_page_erase(INT_FLASH, test_page) \endcode + * and optionally check its return value for STATUS_OK. + * + * \subsection nvm_basic_use_case_usage_code_config Example code: Reading + *configuration of non volatile memory + * Use in application C-file: + * \code + * uint8_t mem_size, page_size, page_num; + * + * nvm_get_size(INT_FLASH, &mem_size); + * nvm_get_page_size(INT_FLASH, &page_size); + * nvm_get_pagenumber(INT_FLASH, test_address, &page_num); + * \endcode + * + * \subsection nvm_basic_use_case_usage_flow Workflow + * -# Prepare a buffer to store configuration of non volatile memory + * \code uint8_t mem_size, page_size, page_num; \endcode + * -# Call nvm_get_size \code nvm_get_size(INT_FLASH, &mem_size); \endcode + * and optionally check its return value for STATUS_OK. + * The memory size of the non volatile memory is in mem_size. + * -# Call nvm_get_page_size \code nvm_get_page_size(INT_FLASH, &page_size); + * \endcode + * and optionally check its return value for STATUS_OK. + * The page size of the non volatile memory is in page_size. + * -# Call nvm_get_pagenumber \code nvm_get_page_number(INT_FLASH, test_address, + * &page_num); \endcode + * and optionally check its return value for STATUS_OK. + * The page number of given address in the non volatile memory is in page_num. + * + * \subsection nvm_basic_use_case_usage_code_locking Example code: Enabling + * security bit + * Use in the application C-file: + * \code + * if(nvm_set_security_bit() == STATUS_OK) + * do_something(); + * \endcode + * + * \subsection nvm_basic_use_case_usage_flow Workflow + * -# Call nvm_set_security_bit \code nvm_set_security_bit() \endcode + * and optionally check its return value for STATUS_OK. + */ + +#ifdef __cplusplus +} +#endif + +#endif /* COMMON_NVM_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/ASF/common/drivers/nvm/xmega/xmega_nvm.c b/bacnet-stack/ports/xplained/ASF/common/drivers/nvm/xmega/xmega_nvm.c new file mode 100644 index 00000000..e4db465b --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/drivers/nvm/xmega/xmega_nvm.c @@ -0,0 +1,331 @@ +/** + * \file + * + * \brief Non volatile memories management for XMEGA devices + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include "common_nvm.h" +#include "conf_nvm.h" +#include "nvm.h" + +status_code_t nvm_init(mem_type_t mem) +{ + switch (mem) { + case INT_FLASH: + case INT_USERPAGE: + case INT_EEPROM: + /* No initialization required for internal memory */ + break; + +#if defined(USE_EXTMEM) && defined(CONF_BOARD_AT45DBX) + case AT45DBX: + /* Initialize dataflash */ + at45dbx_init(); + /* Perform memory check */ + if (!at45dbx_mem_check()) { + return ERR_NO_MEMORY; + } + break; +#endif + + default: + return ERR_INVALID_ARG; + } + + return STATUS_OK; +} + +status_code_t nvm_read_char(mem_type_t mem, uint32_t address, uint8_t *data) +{ + switch (mem) { + case INT_FLASH: + *data = nvm_flash_read_byte((flash_addr_t)address); + break; + + case INT_USERPAGE: + nvm_user_sig_read_buffer((flash_addr_t)address, (void *)data, + 1); + break; + + case INT_EEPROM: + *data = nvm_eeprom_read_byte((eeprom_addr_t)address); + break; + +#if defined(USE_EXTMEM) && defined(CONF_BOARD_AT45DBX) + case AT45DBX: + if (!at45dbx_read_byte_open(address)) { + return ERR_BAD_ADDRESS; + } + + *data = at45dbx_read_byte(); + at45dbx_read_close(); + break; +#endif + + default: + return ERR_INVALID_ARG; + } + + return STATUS_OK; +} + +status_code_t nvm_write_char(mem_type_t mem, uint32_t address, uint8_t data) +{ + switch (mem) { + case INT_FLASH: + nvm_flash_erase_and_write_buffer((flash_addr_t)address, + (const void *)&data, 1, true); + break; + + case INT_USERPAGE: + nvm_user_sig_write_buffer((flash_addr_t)address, + (const void *)&data, 1, true); + break; + + case INT_EEPROM: + nvm_eeprom_write_byte((eeprom_addr_t)address, data); + break; + +#if defined(USE_EXTMEM) && defined(CONF_BOARD_AT45DBX) + case AT45DBX: + if (!at45dbx_write_byte_open(address)) { + return ERR_BAD_ADDRESS; + } + + at45dbx_write_byte(data); + at45dbx_write_close(); +#endif + + default: + return ERR_INVALID_ARG; + } + + return STATUS_OK; +} + +status_code_t nvm_read(mem_type_t mem, uint32_t address, void *buffer, + uint32_t len) +{ + switch (mem) { + case INT_FLASH: + nvm_flash_read_buffer((flash_addr_t)address, buffer, + (uint16_t)len); + break; + + case INT_USERPAGE: + nvm_user_sig_read_buffer((flash_addr_t)address, buffer, + (uint16_t)len); + break; + + case INT_EEPROM: + nvm_eeprom_read_buffer((eeprom_addr_t)address, buffer, + (uint16_t)len); + break; + +#if defined(USE_EXTMEM) && defined(CONF_BOARD_AT45DBX) + case AT45DBX: + { + uint32_t sector = address / AT45DBX_SECTOR_SIZE; + if (!at45dbx_read_sector_open(sector)) { + return ERR_BAD_ADDRESS; + } + + at45dbx_read_sector_to_ram(buffer); + at45dbx_read_close(); + } + break; +#endif + + default: + return ERR_INVALID_ARG; + } + + return STATUS_OK; +} + +status_code_t nvm_write(mem_type_t mem, uint32_t address, void *buffer, + uint32_t len) +{ + switch (mem) { + case INT_FLASH: + nvm_flash_erase_and_write_buffer((flash_addr_t)address, + (const void *)buffer, len, true); + break; + + case INT_USERPAGE: + nvm_user_sig_write_buffer((flash_addr_t)address, + (const void *)buffer, len, true); + break; + + case INT_EEPROM: + nvm_eeprom_erase_and_write_buffer((eeprom_addr_t)address, + (const void *)buffer, len); + break; + +#if defined(USE_EXTMEM) && defined(CONF_BOARD_AT45DBX) + case AT45DBX: + { + uint32_t sector = address / AT45DBX_SECTOR_SIZE; + if (!at45dbx_write_sector_open(sector)) { + return ERR_BAD_ADDRESS; + } + + at45dbx_write_sector_from_ram((const void *)buffer); + at45dbx_write_close(); + } + break; +#endif + + default: + return ERR_INVALID_ARG; + } + + return STATUS_OK; +} + +status_code_t nvm_page_erase(mem_type_t mem, uint32_t page_number) +{ + switch (mem) { + case INT_FLASH: + if ((page_number >= 0) && + (page_number < + (BOOT_SECTION_START / FLASH_PAGE_SIZE))) { + nvm_flash_erase_app_page((flash_addr_t)(page_number * + FLASH_PAGE_SIZE)); + } else if ((page_number >= 0) && + (page_number < + (BOOT_SECTION_END / FLASH_PAGE_SIZE))) { + nvm_flash_erase_boot_page((flash_addr_t)(page_number * + FLASH_PAGE_SIZE)); + } else { + return ERR_INVALID_ARG; + } + + break; + + case INT_USERPAGE: + nvm_flash_erase_user_section(); + break; + + case INT_EEPROM: + nvm_eeprom_erase_page((uint8_t)page_number); + break; + + default: + return ERR_INVALID_ARG; + } + + return STATUS_OK; +} + +status_code_t nvm_get_size(mem_type_t mem, uint32_t *size) +{ + switch (mem) { + case INT_FLASH: + *size = (uint32_t)FLASH_SIZE; + break; + + case INT_USERPAGE: + *size = (uint32_t)FLASH_PAGE_SIZE; + break; + + case INT_EEPROM: + *size = (uint32_t)EEPROM_SIZE; + break; + +#if defined(USE_EXTMEM) && defined(CONF_BOARD_AT45DBX) + case AT45DBX: + *size = (uint32_t)AT45DBX_MEM_SIZE; + break; +#endif + + default: + /* Other memories not supported */ + return ERR_INVALID_ARG; + } + + return STATUS_OK; +} + +status_code_t nvm_get_page_size(mem_type_t mem, uint32_t *size) +{ + switch (mem) { + case INT_FLASH: + case INT_USERPAGE: + *size = (uint32_t)FLASH_PAGE_SIZE; + break; + + case INT_EEPROM: + *size = (uint32_t)EEPROM_PAGE_SIZE; + break; + + default: + /* Other memories not supported */ + return ERR_INVALID_ARG; + } + + return STATUS_OK; +} + +status_code_t nvm_get_pagenumber(mem_type_t mem, uint32_t address, + uint32_t *num) +{ + switch (mem) { + case INT_FLASH: + *num = (uint32_t)(address / FLASH_PAGE_SIZE); + break; + + case INT_EEPROM: + *num = (uint32_t)(address / EEPROM_PAGE_SIZE); + break; + + default: + /* Other memories not supported */ + return ERR_INVALID_ARG; + } + + return STATUS_OK; +} + +status_code_t nvm_set_security_bit(void) +{ + /* Block external programming access to the device */ + nvm_lb_lock_bits_write(NVM_LB_RWLOCK_gc); + return STATUS_OK; +} diff --git a/bacnet-stack/ports/xplained/ASF/common/services/clock/genclk.h b/bacnet-stack/ports/xplained/ASF/common/services/clock/genclk.h new file mode 100644 index 00000000..8df35f2d --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/clock/genclk.h @@ -0,0 +1,180 @@ +/** + * \file + * + * \brief Generic clock management + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef CLK_GENCLK_H_INCLUDED +#define CLK_GENCLK_H_INCLUDED + +#include "parts.h" + +#if SAM3S +# include "sam3s/genclk.h" +#elif SAM3U +# include "sam3u/genclk.h" +#elif SAM3N +# include "sam3n/genclk.h" +#elif SAM3XA +# include "sam3x/genclk.h" +#elif SAM4S +# include "sam4s/genclk.h" +#elif SAM4L +# include "sam4l/genclk.h" +#elif SAM4E +# include "sam4e/genclk.h" +#elif (UC3A0 || UC3A1) +# include "uc3a0_a1/genclk.h" +#elif UC3A3 +# include "uc3a3_a4/genclk.h" +#elif UC3B +# include "uc3b0_b1/genclk.h" +#elif UC3C +# include "uc3c/genclk.h" +#elif UC3D +# include "uc3d/genclk.h" +#elif UC3L +# include "uc3l/genclk.h" +#else +# error Unsupported chip type +#endif + +/** + * \ingroup clk_group + * \defgroup genclk_group Generic Clock Management + * + * Generic clocks are configurable clocks which run outside the system + * clock domain. They are often connected to peripherals which have an + * asynchronous component running independently of the bus clock, e.g. + * USB controllers, low-power timers and RTCs, etc. + * + * Note that not all platforms have support for generic clocks; on such + * platforms, this API will not be available. + * + * @{ + */ + +/** + * \def GENCLK_DIV_MAX + * \brief Maximum divider supported by the generic clock implementation + */ +/** + * \enum genclk_source + * \brief Generic clock source ID + * + * Each generic clock may be generated from a different clock source. + * These are the available alternatives provided by the chip. + */ + +//! \name Generic clock configuration +//@{ +/** + * \struct genclk_config + * \brief Hardware representation of a set of generic clock parameters + */ +/** + * \fn void genclk_config_defaults(struct genclk_config *cfg, + * unsigned int id) + * \brief Initialize \a cfg to the default configuration for the clock + * identified by \a id. + */ +/** + * \fn void genclk_config_read(struct genclk_config *cfg, unsigned int id) + * \brief Read the currently active configuration of the clock + * identified by \a id into \a cfg. + */ +/** + * \fn void genclk_config_write(const struct genclk_config *cfg, + * unsigned int id) + * \brief Activate the configuration \a cfg on the clock identified by + * \a id. + */ +/** + * \fn void genclk_config_set_source(struct genclk_config *cfg, + * enum genclk_source src) + * \brief Select a new source clock \a src in configuration \a cfg. + */ +/** + * \fn void genclk_config_set_divider(struct genclk_config *cfg, + * unsigned int divider) + * \brief Set a new \a divider in configuration \a cfg. + */ +/** + * \fn void genclk_enable_source(enum genclk_source src) + * \brief Enable the source clock \a src used by a generic clock. + */ + //@} + +//! \name Enabling and disabling Generic Clocks +//@{ +/** + * \fn void genclk_enable(const struct genclk_config *cfg, unsigned int id) + * \brief Activate the configuration \a cfg on the clock identified by + * \a id and enable it. + */ +/** + * \fn void genclk_disable(unsigned int id) + * \brief Disable the generic clock identified by \a id. + */ +//@} + +/** + * \brief Enable the configuration defined by \a src and \a divider + * for the generic clock identified by \a id. + * + * \param id The ID of the generic clock. + * \param src The source clock of the generic clock. + * \param divider The divider used to generate the generic clock. + */ +static inline void +genclk_enable_config (unsigned int id, enum genclk_source src, + unsigned int divider) +{ + struct genclk_config gcfg; + + genclk_config_defaults (&gcfg, id); + genclk_enable_source (src); + genclk_config_set_source (&gcfg, src); + genclk_config_set_divider (&gcfg, divider); + genclk_enable (&gcfg, id); +} + +//! @} + +#endif /* CLK_GENCLK_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/clock/osc.h b/bacnet-stack/ports/xplained/ASF/common/services/clock/osc.h new file mode 100644 index 00000000..70a192ee --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/clock/osc.h @@ -0,0 +1,166 @@ +/** + * \file + * + * \brief Oscillator management + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef OSC_H_INCLUDED +#define OSC_H_INCLUDED + +#include "parts.h" +#include "conf_clock.h" + +#if SAM3S +# include "sam3s/osc.h" +#elif SAM3XA +# include "sam3x/osc.h" +#elif SAM3U +# include "sam3u/osc.h" +#elif SAM3N +# include "sam3n/osc.h" +#elif SAM4S +# include "sam4s/osc.h" +#elif SAM4E +# include "sam4e/osc.h" +#elif SAM4L +# include "sam4l/osc.h" +#elif (UC3A0 || UC3A1) +# include "uc3a0_a1/osc.h" +#elif UC3A3 +# include "uc3a3_a4/osc.h" +#elif UC3B +# include "uc3b0_b1/osc.h" +#elif UC3C +# include "uc3c/osc.h" +#elif UC3D +# include "uc3d/osc.h" +#elif UC3L +# include "uc3l/osc.h" +#elif XMEGA +# include "xmega/osc.h" +#else +# error Unsupported chip type +#endif + +/** + * \ingroup clk_group + * \defgroup osc_group Oscillator Management + * + * This group contains functions and definitions related to configuring + * and enabling/disabling on-chip oscillators. Internal RC-oscillators, + * external crystal oscillators and external clock generators are + * supported by this module. What all of these have in common is that + * they swing at a fixed, nominal frequency which is normally not + * adjustable. + * + * \par Example: Enabling an oscillator + * + * The following example demonstrates how to enable the external + * oscillator on XMEGA A and wait for it to be ready to use. The + * oscillator identifiers are platform-specific, so while the same + * procedure is used on all platforms, the parameter to osc_enable() + * will be different from device to device. + * \code + osc_enable(OSC_ID_XOSC); + osc_wait_ready(OSC_ID_XOSC); \endcode + * + * \section osc_group_board Board-specific Definitions + * If external oscillators are used, the board code must provide the + * following definitions for each of those: + * - \b BOARD__HZ: The nominal frequency of the oscillator. + * - \b BOARD__STARTUP_US: The startup time of the + * oscillator in microseconds. + * - \b BOARD__TYPE: The type of oscillator connected, i.e. + * whether it's a crystal or external clock, and sometimes what kind + * of crystal it is. The meaning of this value is platform-specific. + * + * @{ + */ + +//! \name Oscillator Management +//@{ +/** + * \fn void osc_enable(uint8_t id) + * \brief Enable oscillator \a id + * + * The startup time and mode value is automatically determined based on + * definitions in the board code. + */ +/** + * \fn void osc_disable(uint8_t id) + * \brief Disable oscillator \a id + */ +/** + * \fn osc_is_ready(uint8_t id) + * \brief Determine whether oscillator \a id is ready. + * \retval true Oscillator \a id is running and ready to use as a clock + * source. + * \retval false Oscillator \a id is not running. + */ +/** + * \fn uint32_t osc_get_rate(uint8_t id) + * \brief Return the frequency of oscillator \a id in Hz + */ + +#ifndef __ASSEMBLY__ + +/** + * \brief Wait until the oscillator identified by \a id is ready + * + * This function will busy-wait for the oscillator identified by \a id + * to become stable and ready to use as a clock source. + * + * \param id A number identifying the oscillator to wait for. + */ +static inline void +osc_wait_ready (uint8_t id) +{ + while (!osc_is_ready (id)) + { + /* Do nothing */ + } +} + +#endif /* __ASSEMBLY__ */ + +//@} + +//! @} + +#endif /* OSC_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/clock/pll.h b/bacnet-stack/ports/xplained/ASF/common/services/clock/pll.h new file mode 100644 index 00000000..92e049ae --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/clock/pll.h @@ -0,0 +1,322 @@ +/** + * \file + * + * \brief PLL management + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef CLK_PLL_H_INCLUDED +#define CLK_PLL_H_INCLUDED + +#include "parts.h" +#include "conf_clock.h" + +#if SAM3S +# include "sam3s/pll.h" +#elif SAM3XA +# include "sam3x/pll.h" +#elif SAM3U +# include "sam3u/pll.h" +#elif SAM3N +# include "sam3n/pll.h" +#elif SAM4S +# include "sam4s/pll.h" +#elif SAM4E +# include "sam4e/pll.h" +#elif SAM4L +# include "sam4l/pll.h" +#elif (UC3A0 || UC3A1) +# include "uc3a0_a1/pll.h" +#elif UC3A3 +# include "uc3a3_a4/pll.h" +#elif UC3B +# include "uc3b0_b1/pll.h" +#elif UC3C +# include "uc3c/pll.h" +#elif UC3D +# include "uc3d/pll.h" +#elif (UC3L0128 || UC3L0256 || UC3L3_L4) +# include "uc3l/pll.h" +#elif XMEGA +# include "xmega/pll.h" +#else +# error Unsupported chip type +#endif + +/** + * \ingroup clk_group + * \defgroup pll_group PLL Management + * + * This group contains functions and definitions related to configuring + * and enabling/disabling on-chip PLLs. A PLL will take an input signal + * (the \em source), optionally divide the frequency by a configurable + * \em divider, and then multiply the frequency by a configurable \em + * multiplier. + * + * Some devices don't support input dividers; specifying any other + * divisor than 1 on these devices will result in an assertion failure. + * Other devices may have various restrictions to the frequency range of + * the input and output signals. + * + * \par Example: Setting up PLL0 with default parameters + * + * The following example shows how to configure and enable PLL0 using + * the default parameters specified using the configuration symbols + * listed above. + * \code + pll_enable_config_defaults(0); \endcode + * + * To configure, enable PLL0 using the default parameters and to disable + * a specific feature like Wide Bandwidth Mode (a UC3A3-specific + * PLL option.), you can use this initialization process. + * \code + struct pll_config pllcfg; + if (pll_is_locked(pll_id)) { + return; // Pll already running + } + pll_enable_source(CONFIG_PLL0_SOURCE); + pll_config_defaults(&pllcfg, 0); + pll_config_set_option(&pllcfg, PLL_OPT_WBM_DISABLE); + pll_enable(&pllcfg, 0); + pll_wait_for_lock(0); \endcode + * + * When the last function call returns, PLL0 is ready to be used as the + * main system clock source. + * + * \section pll_group_config Configuration Symbols + * + * Each PLL has a set of default parameters determined by the following + * configuration symbols in the application's configuration file: + * - \b CONFIG_PLLn_SOURCE: The default clock source connected to the + * input of PLL \a n. Must be one of the values defined by the + * #pll_source enum. + * - \b CONFIG_PLLn_MUL: The default multiplier (loop divider) of PLL + * \a n. + * - \b CONFIG_PLLn_DIV: The default input divider of PLL \a n. + * + * These configuration symbols determine the result of calling + * pll_config_defaults() and pll_get_default_rate(). + * + * @{ + */ + +//! \name Chip-specific PLL characteristics +//@{ +/** + * \def PLL_MAX_STARTUP_CYCLES + * \brief Maximum PLL startup time in number of slow clock cycles + */ +/** + * \def NR_PLLS + * \brief Number of on-chip PLLs + */ + +/** + * \def PLL_MIN_HZ + * \brief Minimum frequency that the PLL can generate + */ +/** + * \def PLL_MAX_HZ + * \brief Maximum frequency that the PLL can generate + */ +/** + * \def PLL_NR_OPTIONS + * \brief Number of PLL option bits + */ +//@} + +/** + * \enum pll_source + * \brief PLL clock source + */ + +//! \name PLL configuration +//@{ + +/** + * \struct pll_config + * \brief Hardware-specific representation of PLL configuration. + * + * This structure contains one or more device-specific values + * representing the current PLL configuration. The contents of this + * structure is typically different from platform to platform, and the + * user should not access any fields except through the PLL + * configuration API. + */ + +/** + * \fn void pll_config_init(struct pll_config *cfg, + * enum pll_source src, unsigned int div, unsigned int mul) + * \brief Initialize PLL configuration from standard parameters. + * + * \note This function may be defined inline because it is assumed to be + * called very few times, and usually with constant parameters. Inlining + * it will in such cases reduce the code size significantly. + * + * \param cfg The PLL configuration to be initialized. + * \param src The oscillator to be used as input to the PLL. + * \param div PLL input divider. + * \param mul PLL loop divider (i.e. multiplier). + * + * \return A configuration which will make the PLL run at + * (\a mul / \a div) times the frequency of \a src + */ +/** + * \def pll_config_defaults(cfg, pll_id) + * \brief Initialize PLL configuration using default parameters. + * + * After this function returns, \a cfg will contain a configuration + * which will make the PLL run at (CONFIG_PLLx_MUL / CONFIG_PLLx_DIV) + * times the frequency of CONFIG_PLLx_SOURCE. + * + * \param cfg The PLL configuration to be initialized. + * \param pll_id Use defaults for this PLL. + */ +/** + * \def pll_get_default_rate(pll_id) + * \brief Get the default rate in Hz of \a pll_id + */ +/** + * \fn void pll_config_set_option(struct pll_config *cfg, + * unsigned int option) + * \brief Set the PLL option bit \a option in the configuration \a cfg. + * + * \param cfg The PLL configuration to be changed. + * \param option The PLL option bit to be set. + */ +/** + * \fn void pll_config_clear_option(struct pll_config *cfg, + * unsigned int option) + * \brief Clear the PLL option bit \a option in the configuration \a cfg. + * + * \param cfg The PLL configuration to be changed. + * \param option The PLL option bit to be cleared. + */ +/** + * \fn void pll_config_read(struct pll_config *cfg, unsigned int pll_id) + * \brief Read the currently active configuration of \a pll_id. + * + * \param cfg The configuration object into which to store the currently + * active configuration. + * \param pll_id The ID of the PLL to be accessed. + */ +/** + * \fn void pll_config_write(const struct pll_config *cfg, + * unsigned int pll_id) + * \brief Activate the configuration \a cfg on \a pll_id + * + * \param cfg The configuration object representing the PLL + * configuration to be activated. + * \param pll_id The ID of the PLL to be updated. + */ + +//@} + +//! \name Interaction with the PLL hardware +//@{ +/** + * \fn void pll_enable(const struct pll_config *cfg, + * unsigned int pll_id) + * \brief Activate the configuration \a cfg and enable PLL \a pll_id. + * + * \param cfg The PLL configuration to be activated. + * \param pll_id The ID of the PLL to be enabled. + */ +/** + * \fn void pll_disable(unsigned int pll_id) + * \brief Disable the PLL identified by \a pll_id. + * + * After this function is called, the PLL identified by \a pll_id will + * be disabled. The PLL configuration stored in hardware may be affected + * by this, so if the caller needs to restore the same configuration + * later, it should either do a pll_config_read() before disabling the + * PLL, or remember the last configuration written to the PLL. + * + * \param pll_id The ID of the PLL to be disabled. + */ +/** + * \fn bool pll_is_locked(unsigned int pll_id) + * \brief Determine whether the PLL is locked or not. + * + * \param pll_id The ID of the PLL to check. + * + * \retval true The PLL is locked and ready to use as a clock source + * \retval false The PLL is not yet locked, or has not been enabled. + */ +/** + * \fn void pll_enable_source(enum pll_source src) + * \brief Enable the source of the pll. + * The source is enabled, if the source is not already running. + * + * \param src The ID of the PLL source to enable. + */ +/** + * \fn void pll_enable_config_defaults(unsigned int pll_id) + * \brief Enable the pll with the default configuration. + * PLL is enabled, if the PLL is not already locked. + * + * \param pll_id The ID of the PLL to enable. + */ + +/** + * \brief Wait for PLL \a pll_id to become locked + * + * \todo Use a timeout to avoid waiting forever and hanging the system + * + * \param pll_id The ID of the PLL to wait for. + * + * \retval STATUS_OK The PLL is now locked. + * \retval ERR_TIMEOUT Timed out waiting for PLL to become locked. + */ +static inline int +pll_wait_for_lock (unsigned int pll_id) +{ + Assert (pll_id < NR_PLLS); + + while (!pll_is_locked (pll_id)) + { + /* Do nothing */ + } + + return 0; +} + +//@} +//! @} + +#endif /* CLK_PLL_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/clock/sysclk.h b/bacnet-stack/ports/xplained/ASF/common/services/clock/sysclk.h new file mode 100644 index 00000000..6a125a8b --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/clock/sysclk.h @@ -0,0 +1,173 @@ +/** + * \file + * + * \brief System clock management + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef SYSCLK_H_INCLUDED +#define SYSCLK_H_INCLUDED + +#include "parts.h" +#include "conf_clock.h" + +#if SAM3S +# include "sam3s/sysclk.h" +#elif SAM3U +# include "sam3u/sysclk.h" +#elif SAM3N +# include "sam3n/sysclk.h" +#elif SAM3XA +# include "sam3x/sysclk.h" +#elif SAM4S +# include "sam4s/sysclk.h" +#elif SAM4E +# include "sam4e/sysclk.h" +#elif SAM4L +# include "sam4l/sysclk.h" +#elif (UC3A0 || UC3A1) +# include "uc3a0_a1/sysclk.h" +#elif UC3A3 +# include "uc3a3_a4/sysclk.h" +#elif UC3B +# include "uc3b0_b1/sysclk.h" +#elif UC3C +# include "uc3c/sysclk.h" +#elif UC3D +# include "uc3d/sysclk.h" +#elif UC3L +# include "uc3l/sysclk.h" +#elif XMEGA +# include "xmega/sysclk.h" +#elif MEGA +# include "mega/sysclk.h" +#else +# error Unsupported chip type +#endif + +/** + * \defgroup clk_group Clock Management + */ + +/** + * \ingroup clk_group + * \defgroup sysclk_group System Clock Management + * + * See \ref sysclk_quickstart. + * + * The sysclk API covers the system clock and all + * clocks derived from it. The system clock is a chip-internal clock on + * which all synchronous clocks, i.e. CPU and bus/peripheral + * clocks, are based. The system clock is typically generated from one + * of a variety of sources, which may include crystal and RC oscillators + * as well as PLLs. The clocks derived from the system clock are + * sometimes also known as synchronous clocks, since they + * always run synchronously with respect to each other, as opposed to + * generic clocks which may run from different oscillators or + * PLLs. + * + * Most applications should simply call sysclk_init() to initialize + * everything related to the system clock and its source (oscillator, + * PLL or DFLL), and leave it at that. More advanced applications, and + * platform-specific drivers, may require additional services from the + * clock system, some of which may be platform-specific. + * + * \section sysclk_group_platform Platform Dependencies + * + * The sysclk API is partially chip- or platform-specific. While all + * platforms provide mostly the same functionality, there are some + * variations around how different bus types and clock tree structures + * are handled. + * + * The following functions are available on all platforms with the same + * parameters and functionality. These functions may be called freely by + * portable applications, drivers and services: + * - sysclk_init() + * - sysclk_set_source() + * - sysclk_get_main_hz() + * - sysclk_get_cpu_hz() + * - sysclk_get_peripheral_bus_hz() + * + * The following functions are available on all platforms, but there may + * be variations in the function signature (i.e. parameters) and + * behavior. These functions are typically called by platform-specific + * parts of drivers, and applications that aren't intended to be + * portable: + * - sysclk_enable_peripheral_clock() + * - sysclk_disable_peripheral_clock() + * - sysclk_enable_module() + * - sysclk_disable_module() + * - sysclk_module_is_enabled() + * - sysclk_set_prescalers() + * + * All other functions should be considered platform-specific. + * Enabling/disabling clocks to specific peripherals as well as + * determining the speed of these clocks should be done by calling + * functions provided by the driver for that peripheral. + * + * @{ + */ + +//! \name System Clock Initialization +//@{ +/** + * \fn void sysclk_init(void) + * \brief Initialize the synchronous clock system. + * + * This function will initialize the system clock and its source. This + * includes: + * - Mask all synchronous clocks except for any clocks which are + * essential for normal operation (for example internal memory + * clocks). + * - Set up the system clock prescalers as specified by the + * application's configuration file. + * - Enable the clock source specified by the application's + * configuration file (oscillator or PLL) and wait for it to become + * stable. + * - Set the main system clock source to the clock specified by the + * application's configuration file. + * + * Since all non-essential peripheral clocks are initially disabled, it + * is the responsibility of the peripheral driver to re-enable any + * clocks that are needed for normal operation. + */ +//@} + +//! @} + +#endif /* SYSCLK_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/osc.h b/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/osc.h new file mode 100644 index 00000000..5e072c9c --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/osc.h @@ -0,0 +1,513 @@ +/** + * \file + * + * \brief Chip-specific oscillator management functions + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef XMEGA_OSC_H_INCLUDED +#define XMEGA_OSC_H_INCLUDED + +#include +#include + +/** + * \weakgroup osc_group + * + * \section osc_group_errata Errata + * - Auto-calibration does not work on XMEGA A1 revision H and + * earlier. + * @{ + */ + +//! \name Oscillator identifiers +//@{ +//! 2 MHz Internal RC Oscillator +#define OSC_ID_RC2MHZ OSC_RC2MEN_bm +//! 32 MHz Internal RC Oscillator +#define OSC_ID_RC32MHZ OSC_RC32MEN_bm +//! 32 KHz Internal RC Oscillator +#define OSC_ID_RC32KHZ OSC_RC32KEN_bm +//! External Oscillator +#define OSC_ID_XOSC OSC_XOSCEN_bm +#if XMEGA_E +//! 8 MHz Internal RC Oscillator +# define OSC_ID_RC8MHZ OSC_RC8MEN_bm +#endif + +/** + * \brief Reference from USB Start Of Frame + * \note This cannot be enabled or disabled, but can be used as a reference for + * the autocalibration (DFLL). + */ +#define OSC_ID_USBSOF 0xff +//@} + +//! \name External oscillator types +//@{ +#define XOSC_TYPE_EXTERNAL 0 //!< External clock signal +#define XOSC_TYPE_32KHZ 2 //!< 32.768 kHz resonator on TOSC +#define XOSC_TYPE_XTAL 3 //!< 0.4 to 16 MHz resonator on XTAL +//@} + +/** + * \def CONFIG_XOSC_32KHZ_LPM + * \brief Define for enabling Low Power Mode for 32 kHz external oscillator. + */ +#ifdef __DOXYGEN__ +# define CONFIG_XOSC_32KHZ_LPM +#endif /* __DOXYGEN__ */ + +/** + * \def CONFIG_XOSC_STARTUP + * \brief Board-dependent value that determines the number of start-up cycles + * for external resonators, based on BOARD_XOSC_STARTUP_US. This is written to + * the two MSB of the XOSCSEL field of OSC.XOSCCTRL. + * + * \note This is automatically computed from BOARD_XOSC_HZ and + * BOARD_XOSC_STARTUP_US if it is not manually set. + */ + +//! \name XTAL resonator start-up cycles +//@{ +#define XOSC_STARTUP_256 0 //!< 256 cycle start-up time +#define XOSC_STARTUP_1024 1 //!< 1 k cycle start-up time +#define XOSC_STARTUP_16384 2 //!< 16 k cycle start-up time +//@} + +/** + * \def CONFIG_XOSC_RANGE + * \brief Board-dependent value that sets the frequency range of the external + * oscillator. This is written to the FRQRANGE field of OSC.XOSCCTRL. + * + * \note This is automatically computed from BOARD_XOSC_HZ if it is not manually + * set. + */ + +//! \name XTAL resonator frequency range +//@{ +//! 0.4 to 2 MHz frequency range +#define XOSC_RANGE_04TO2 OSC_FRQRANGE_04TO2_gc +//! 2 to 9 MHz frequency range +#define XOSC_RANGE_2TO9 OSC_FRQRANGE_2TO9_gc +//! 9 to 12 MHz frequency range +#define XOSC_RANGE_9TO12 OSC_FRQRANGE_9TO12_gc +//! 12 to 16 MHz frequency range +#define XOSC_RANGE_12TO16 OSC_FRQRANGE_12TO16_gc +//@} + +/** + * \def XOSC_STARTUP_TIMEOUT + * \brief Number of us to wait for XOSC to start + * + * This is the number of slow clock cycles corresponding to + * OSC0_STARTUP_VALUE with an additional 25% safety margin. If the + * oscillator isn't running when this timeout has expired, it is assumed + * to have failed to start. + */ + +// If application intends to use XOSC. +#ifdef BOARD_XOSC_HZ +// Get start-up config for XOSC, if not manually set. +# ifndef CONFIG_XOSC_STARTUP +# ifndef BOARD_XOSC_STARTUP_US +# error BOARD_XOSC_STARTUP_US must be configured. +# else +//! \internal Number of start-up cycles for the board's XOSC. +# define BOARD_XOSC_STARTUP_CYCLES \ + (BOARD_XOSC_HZ / 1000000 * BOARD_XOSC_STARTUP_US) + +# if (BOARD_XOSC_TYPE == XOSC_TYPE_XTAL) +# if (BOARD_XOSC_STARTUP_CYCLES > 16384) +# error BOARD_XOSC_STARTUP_US is too high for current BOARD_XOSC_HZ. + +# elif (BOARD_XOSC_STARTUP_CYCLES > 1024) +# define CONFIG_XOSC_STARTUP XOSC_STARTUP_16384 +# define XOSC_STARTUP_TIMEOUT (16384*(1000000/BOARD_XOSC_HZ)) + +# elif (BOARD_XOSC_STARTUP_CYCLES > 256) +# define CONFIG_XOSC_STARTUP XOSC_STARTUP_1024 +# define XOSC_STARTUP_TIMEOUT (1024*(1000000/BOARD_XOSC_HZ)) + +# else +# define CONFIG_XOSC_STARTUP XOSC_STARTUP_256 +# define XOSC_STARTUP_TIMEOUT (256*(1000000/BOARD_XOSC_HZ)) +# endif +# else /* BOARD_XOSC_TYPE == XOSC_TYPE_XTAL */ +# define CONFIG_XOSC_STARTUP 0 +# endif +# endif /* BOARD_XOSC_STARTUP_US */ +# endif /* CONFIG_XOSC_STARTUP */ + +// Get frequency range setting for XOSC, if not manually set. +# ifndef CONFIG_XOSC_RANGE +# if (BOARD_XOSC_TYPE == XOSC_TYPE_XTAL) +# if (BOARD_XOSC_HZ < 400000) +# error BOARD_XOSC_HZ is below minimum frequency of 400 kHz. + +# elif (BOARD_XOSC_HZ < 2000000) +# define CONFIG_XOSC_RANGE XOSC_RANGE_04TO2 + +# elif (BOARD_XOSC_HZ < 9000000) +# define CONFIG_XOSC_RANGE XOSC_RANGE_2TO9 + +# elif (BOARD_XOSC_HZ < 12000000) +# define CONFIG_XOSC_RANGE XOSC_RANGE_9TO12 + +# elif (BOARD_XOSC_HZ <= 16000000) +# define CONFIG_XOSC_RANGE XOSC_RANGE_12TO16 + +# else +# error BOARD_XOSC_HZ is above maximum frequency of 16 MHz. +# endif +# else /* BOARD_XOSC_TYPE == XOSC_TYPE_XTAL */ +# define CONFIG_XOSC_RANGE 0 +# endif +# endif /* CONFIG_XOSC_RANGE */ +#endif /* BOARD_XOSC_HZ */ + +#ifndef __ASSEMBLY__ + +/** + * \internal + * \brief Enable internal oscillator \a id + * + * Do not call this function directly. Use osc_enable() instead. + */ +static inline void +osc_enable_internal (uint8_t id) +{ + irqflags_t flags; + + Assert (id != OSC_ID_USBSOF); + + flags = cpu_irq_save (); + OSC.CTRL |= id; +#if (XMEGA_E && CONFIG_SYSCLK_RC8MHZ_LPM) + if (id == OSC_ID_RC8MHZ) + { + OSC.CTRL |= OSC_RC8MLPM_bm; + } +#endif + cpu_irq_restore (flags); +} + +#if defined(BOARD_XOSC_HZ) || defined(__DOXYGEN__) + +/** + * \internal + * \brief Enable external oscillator \a id + * + * Do not call this function directly. Use osc_enable() instead. Also + * note that this function is only available if the board actually has + * an external oscillator crystal. + */ +static inline void +osc_enable_external (uint8_t id) +{ + irqflags_t flags; + + Assert (id == OSC_ID_XOSC); + +#ifndef CONFIG_XOSC_32KHZ_LPM +# if (XMEGA_E && (BOARD_XOSC_TYPE == XOSC_TYPE_EXTERNAL) && defined(CONFIG_XOSC_EXTERNAL_PC4)) + OSC.XOSCCTRL = OSC_XOSCSEL4_bm; +# else + OSC.XOSCCTRL = BOARD_XOSC_TYPE | (CONFIG_XOSC_STARTUP << 2) | + CONFIG_XOSC_RANGE; +# endif +#else + OSC.XOSCCTRL = BOARD_XOSC_TYPE | (CONFIG_XOSC_STARTUP << 2) | + CONFIG_XOSC_RANGE | OSC_X32KLPM_bm; +#endif /* CONFIG_XOSC_32KHZ_LPM */ + + flags = cpu_irq_save (); + OSC.CTRL |= id; + cpu_irq_restore (flags); +} +#else + +static inline void +osc_enable_external (uint8_t id) +{ + Assert (false); // No external oscillator on the selected board +} +#endif + +static inline void +osc_disable (uint8_t id) +{ + irqflags_t flags; + + Assert (id != OSC_ID_USBSOF); + + flags = cpu_irq_save (); + OSC.CTRL &= ~id; + cpu_irq_restore (flags); +} + +static inline void +osc_enable (uint8_t id) +{ + if (id != OSC_ID_XOSC) + { + osc_enable_internal (id); + } + else + { + osc_enable_external (id); + } +} + +static inline bool +osc_is_ready (uint8_t id) +{ + Assert (id != OSC_ID_USBSOF); + + return OSC.STATUS & id; +} + +//! \name XMEGA-Specific Oscillator Features +//@{ + +/** + * \brief Enable DFLL-based automatic calibration of an internal + * oscillator. + * + * The XMEGA features two Digital Frequency Locked Loops (DFLLs) which + * can be used to improve the accuracy of the 2 MHz and 32 MHz internal + * RC oscillators. The DFLL compares the oscillator frequency with a + * more accurate reference clock to do automatic run-time calibration of + * the oscillator. + * + * This function enables auto-calibration for either the 2 MHz or 32 MHz + * internal oscillator using either the 32.768 kHz calibrated internal + * oscillator or an external crystal oscillator as a reference. If the + * latter option is used, the crystal must be connected to the TOSC pins + * and run at 32.768 kHz. + * + * \param id The ID of the oscillator for which to enable + * auto-calibration: + * \arg \c OSC_ID_RC2MHZ or \c OSC_ID_RC32MHZ. + * \param ref_id The ID of the oscillator to use as a reference: + * \arg \c OSC_ID_RC32KHZ or \c OSC_ID_XOSC for internal or external 32 kHz + * reference, respectively. + * \arg \c OSC_ID_USBSOF for 32 MHz only when USB is available and running. + */ +static inline void +osc_enable_autocalibration (uint8_t id, uint8_t ref_id) +{ + irqflags_t flags; + + flags = cpu_irq_save (); + switch (id) + { + case OSC_ID_RC2MHZ: +#if !XMEGA_E + Assert ((ref_id == OSC_ID_RC32KHZ) || (ref_id == OSC_ID_XOSC)); + if (ref_id == OSC_ID_XOSC) + { + osc_enable (OSC_ID_RC32KHZ); + OSC.DFLLCTRL |= OSC_RC2MCREF_bm; + } + else + { + OSC.DFLLCTRL &= ~(OSC_RC2MCREF_bm); + } + DFLLRC2M.CTRL |= DFLL_ENABLE_bm; +#endif + break; + + case OSC_ID_RC32MHZ: +#if XMEGA_AU || XMEGA_B || XMEGA_C || XMEGA_E + Assert ((ref_id == OSC_ID_RC32KHZ) + || (ref_id == OSC_ID_XOSC) || (ref_id == OSC_ID_USBSOF)); + + OSC.DFLLCTRL &= ~(OSC_RC32MCREF_gm); + + if (ref_id == OSC_ID_XOSC) + { + osc_enable (OSC_ID_RC32KHZ); + OSC.DFLLCTRL |= OSC_RC32MCREF_XOSC32K_gc; + } + else if (ref_id == OSC_ID_RC32KHZ) + { + OSC.DFLLCTRL |= OSC_RC32MCREF_RC32K_gc; + } +# if !XMEGA_E + else if (ref_id == OSC_ID_USBSOF) + { + /* + * Calibrate 32MRC at 48MHz using USB SOF + * 48MHz / 1kHz = 0xBB80 + */ + DFLLRC32M.COMP1 = 0x80; + DFLLRC32M.COMP2 = 0xBB; + OSC.DFLLCTRL |= OSC_RC32MCREF_USBSOF_gc; + } +# endif +#else + Assert ((ref_id == OSC_ID_RC32KHZ) || (ref_id == OSC_ID_XOSC)); + + if (ref_id == OSC_ID_XOSC) + { + osc_enable (OSC_ID_RC32KHZ); + OSC.DFLLCTRL |= OSC_RC32MCREF_bm; + } + else if (ref_id == OSC_ID_RC32KHZ) + { + OSC.DFLLCTRL &= ~(OSC_RC32MCREF_bm); + } +#endif + DFLLRC32M.CTRL |= DFLL_ENABLE_bm; + break; + + default: + Assert (false); + break; + } + cpu_irq_restore (flags); +} + +/** + * \brief Disable DFLL-based automatic calibration of an internal + * oscillator. + * + * \see osc_enable_autocalibration + * + * \param id The ID of the oscillator for which to disable + * auto-calibration: + * \arg \c OSC_ID_RC2MHZ or \c OSC_ID_RC32MHZ. + */ +static inline void +osc_disable_autocalibration (uint8_t id) +{ + switch (id) + { + case OSC_ID_RC2MHZ: +#if !XMEGA_E + DFLLRC2M.CTRL = 0; +#endif + break; + + case OSC_ID_RC32MHZ: + DFLLRC32M.CTRL = 0; + break; + + default: + Assert (false); + break; + } +} + +/** + * \brief Load a specific calibration value for the specified oscillator. + * + * \param id The ID of the oscillator for which to disable + * auto-calibration: + * \arg \c OSC_ID_RC2MHZ or \c OSC_ID_RC32MHZ. + * \param calib The specific calibration value required: + * + */ +static inline void +osc_user_calibration (uint8_t id, uint16_t calib) +{ + switch (id) + { + case OSC_ID_RC2MHZ: +#if !XMEGA_E + DFLLRC2M.CALA = LSB (calib); + DFLLRC2M.CALB = MSB (calib); +#endif + break; + + case OSC_ID_RC32MHZ: + DFLLRC32M.CALA = LSB (calib); + DFLLRC32M.CALB = MSB (calib); + break; + +#if XMEGA_E + case OSC_ID_RC8MHZ: + OSC.RC8MCAL = LSB (calib); + break; +#endif + + default: + Assert (false); + break; + } +} + +//@} + +static inline uint32_t +osc_get_rate (uint8_t id) +{ + Assert (id != OSC_ID_USBSOF); + + switch (id) + { + case OSC_ID_RC2MHZ: + return 2000000UL; + + case OSC_ID_RC32MHZ: +#ifdef CONFIG_OSC_RC32_CAL + return CONFIG_OSC_RC32_CAL; +#else + return 32000000UL; +#endif + + case OSC_ID_RC32KHZ: + return 32768UL; + +#ifdef BOARD_XOSC_HZ + case OSC_ID_XOSC: + return BOARD_XOSC_HZ; +#endif + + default: + Assert (false); + return 0; + } +} + +#endif /* __ASSEMBLY__ */ + +//! @} + +#endif /* XMEGA_OSC_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/pll.h b/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/pll.h new file mode 100644 index 00000000..64ee26f0 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/pll.h @@ -0,0 +1,287 @@ +/** + * \file + * + * \brief Chip-specific PLL management functions + * + * Copyright (c) 2010-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef XMEGA_PLL_H_INCLUDED +#define XMEGA_PLL_H_INCLUDED + +#include + +/** + * \weakgroup pll_group + * @{ + */ + +#define NR_PLLS 1 +#define PLL_MIN_HZ 10000000UL +#define PLL_MAX_HZ 200000000UL +#define PLL_NR_OPTIONS 0 + +enum pll_source +{ + //! 2 MHz Internal RC Oscillator + PLL_SRC_RC2MHZ = OSC_PLLSRC_RC2M_gc, + //! 32 MHz Internal RC Oscillator + PLL_SRC_RC32MHZ = OSC_PLLSRC_RC32M_gc, + //! External Clock Source + PLL_SRC_XOSC = OSC_PLLSRC_XOSC_gc, +}; + +#define pll_get_default_rate(pll_id) \ + pll_get_default_rate_priv(CONFIG_PLL##pll_id##_SOURCE, \ + CONFIG_PLL##pll_id##_MUL, \ + CONFIG_PLL##pll_id##_DIV) + +/** + * \internal + * \brief Return clock rate for specified PLL settings. + * + * \note Due to the hardware implementation of the PLL, \a div must be 4 if the + * 32 MHz RC oscillator is used as reference and 1 otherwise. The reference must + * be above 440 kHz, and the output between 10 and 200 MHz. + * + * \param src ID of the PLL's reference source oscillator. + * \param mul Multiplier for the PLL. + * \param div Divisor for the PLL. + * + * \retval Output clock rate from PLL. + */ +static inline uint32_t +pll_get_default_rate_priv (enum pll_source src, + unsigned int mul, unsigned int div) +{ + uint32_t rate; + + switch (src) + { + case PLL_SRC_RC2MHZ: + rate = 2000000UL; + Assert (div == 1); + break; + + case PLL_SRC_RC32MHZ: +#ifdef CONFIG_OSC_RC32_CAL //32MHz oscillator is calibrated to another frequency + rate = CONFIG_OSC_RC32_CAL / 4; +#else + rate = 8000000UL; +#endif + Assert (div == 4); + break; + + case PLL_SRC_XOSC: + rate = osc_get_rate (OSC_ID_XOSC); + Assert (div == 1); + break; + + default: + break; + } + + Assert (rate >= 440000UL); + + rate *= mul; + + Assert (rate >= PLL_MIN_HZ); + Assert (rate <= PLL_MAX_HZ); + + return rate; +} + +struct pll_config +{ + uint8_t ctrl; +}; + +/** + * \note The XMEGA PLL hardware uses hard-wired input dividers, so the + * user must ensure that \a div is set as follows: + * - If \a src is PLL_SRC_32MHZ, \a div must be set to 4. + * - Otherwise, \a div must be set to 1. + */ +static inline void +pll_config_init (struct pll_config *cfg, enum pll_source src, + unsigned int div, unsigned int mul) +{ + Assert (mul >= 1 && mul <= 31); + + if (src == PLL_SRC_RC32MHZ) + { + Assert (div == 4); + } + else + { + Assert (div == 1); + } + + /* Initialize the configuration */ + cfg->ctrl = src | (mul << OSC_PLLFAC_gp); +} + +#define pll_config_defaults(cfg, pll_id) \ + pll_config_init(cfg, \ + CONFIG_PLL##pll_id##_SOURCE, \ + CONFIG_PLL##pll_id##_DIV, \ + CONFIG_PLL##pll_id##_MUL) + +static inline void +pll_config_read (struct pll_config *cfg, unsigned int pll_id) +{ + Assert (pll_id < NR_PLLS); + + cfg->ctrl = OSC.PLLCTRL; +} + +static inline void +pll_config_write (const struct pll_config *cfg, unsigned int pll_id) +{ + Assert (pll_id < NR_PLLS); + + OSC.PLLCTRL = cfg->ctrl; +} + +/** + * \note If a different PLL reference oscillator than those enabled by + * \ref sysclk_init() is used, the user must ensure that the desired reference + * is enabled prior to calling this function. + */ +static inline void +pll_enable (const struct pll_config *cfg, unsigned int pll_id) +{ + irqflags_t flags; + + Assert (pll_id < NR_PLLS); + + flags = cpu_irq_save (); + pll_config_write (cfg, pll_id); + OSC.CTRL |= OSC_PLLEN_bm; + cpu_irq_restore (flags); +} + +/*! \note This will not automatically disable the reference oscillator that is + * configured for the PLL. + */ +static inline void +pll_disable (unsigned int pll_id) +{ + irqflags_t flags; + + Assert (pll_id < NR_PLLS); + + flags = cpu_irq_save (); + OSC.CTRL &= ~OSC_PLLEN_bm; + cpu_irq_restore (flags); +} + +static inline bool +pll_is_locked (unsigned int pll_id) +{ + Assert (pll_id < NR_PLLS); + + return OSC.STATUS & OSC_PLLRDY_bm; +} + +static inline void +pll_enable_source (enum pll_source src) +{ + switch (src) + { + case PLL_SRC_RC2MHZ: + break; + + case PLL_SRC_RC32MHZ: + if (!osc_is_ready (OSC_ID_RC32MHZ)) + { + osc_enable (OSC_ID_RC32MHZ); + osc_wait_ready (OSC_ID_RC32MHZ); +#ifdef CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC + if (CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC != OSC_ID_USBSOF) + { + osc_enable (CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC); + osc_wait_ready (CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC); + } + osc_enable_autocalibration (OSC_ID_RC32MHZ, + CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC); +#endif + } + break; + + case PLL_SRC_XOSC: + if (!osc_is_ready (OSC_ID_XOSC)) + { + osc_enable (OSC_ID_XOSC); + osc_wait_ready (OSC_ID_XOSC); + } + break; + default: + Assert (false); + break; + } +} + +static inline void +pll_enable_config_defaults (unsigned int pll_id) +{ + struct pll_config pllcfg; + + if (pll_is_locked (pll_id)) + { + return; // Pll already running + } + switch (pll_id) + { +#ifdef CONFIG_PLL0_SOURCE + case 0: + pll_enable_source (CONFIG_PLL0_SOURCE); + pll_config_init (&pllcfg, + CONFIG_PLL0_SOURCE, CONFIG_PLL0_DIV, CONFIG_PLL0_MUL); + break; +#endif + default: + Assert (false); + break; + } + pll_enable (&pllcfg, pll_id); + while (!pll_is_locked (pll_id)); +} + +//! @} + +#endif /* XMEGA_PLL_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/sysclk.c b/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/sysclk.c new file mode 100644 index 00000000..c114fe49 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/sysclk.c @@ -0,0 +1,255 @@ +/** + * \file + * + * \brief Chip-specific system clock management functions + * + * Copyright (c) 2010-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#include + +#include +#include +#include + +#if XMEGA_AU || XMEGA_B || XMEGA_C +# include +#endif + + +void sysclk_init(void) +{ + uint8_t *reg = (uint8_t *) & PR.PRGEN; + uint8_t i; +#ifdef CONFIG_OSC_RC32_CAL + uint16_t cal; + /* avoid Cppcheck Warning */ + UNUSED(cal); +#endif + bool need_rc2mhz = false; + + /* Turn off all peripheral clocks that can be turned off. */ + for (i = 0; i <= SYSCLK_PORT_F; i++) { + *(reg++) = 0xff; + } + + /* Set up system clock prescalers if different from defaults */ + if ((CONFIG_SYSCLK_PSADIV != SYSCLK_PSADIV_1) + || (CONFIG_SYSCLK_PSBCDIV != SYSCLK_PSBCDIV_1_1)) { + sysclk_set_prescalers(CONFIG_SYSCLK_PSADIV, CONFIG_SYSCLK_PSBCDIV); + } +#if (CONFIG_OSC_RC32_CAL==48000000UL) + MSB(cal) = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(USBRCOSC)); + LSB(cal) = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(USBRCOSCA)); + /* + * If a device has an uncalibrated value in the + * production signature row (early sample part), load a + * sane default calibration value. + */ + if (cal == 0xFFFF) { + cal = 0x2340; + } + osc_user_calibration(OSC_ID_RC32MHZ, cal); +#endif + /* + * Switch to the selected initial system clock source, unless + * the default internal 2 MHz oscillator is selected. + */ + if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_RC2MHZ) { + need_rc2mhz = true; + } else { + switch (CONFIG_SYSCLK_SOURCE) { + case SYSCLK_SRC_RC32MHZ: + osc_enable(OSC_ID_RC32MHZ); + osc_wait_ready(OSC_ID_RC32MHZ); +#ifdef CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC + if (CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC != OSC_ID_USBSOF) { + osc_enable(CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC); + osc_wait_ready(CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC); + } + osc_enable_autocalibration(OSC_ID_RC32MHZ, + CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC); +#endif + break; + + case SYSCLK_SRC_RC32KHZ: + osc_enable(OSC_ID_RC32KHZ); + osc_wait_ready(OSC_ID_RC32KHZ); + break; + + case SYSCLK_SRC_XOSC: + osc_enable(OSC_ID_XOSC); + osc_wait_ready(OSC_ID_XOSC); + break; + +#ifdef CONFIG_PLL0_SOURCE + case SYSCLK_SRC_PLL: + if (CONFIG_PLL0_SOURCE == PLL_SRC_RC2MHZ) { + need_rc2mhz = true; + } + pll_enable_config_defaults(0); + break; +#endif +#if XMEGA_E + case SYSCLK_SRC_RC8MHZ: + osc_enable(OSC_ID_RC8MHZ); + osc_wait_ready(OSC_ID_RC8MHZ); + break; +#endif + default: + //unhandled_case(CONFIG_SYSCLK_SOURCE); + return; + } + + ccp_write_io((uint8_t *) & CLK.CTRL, CONFIG_SYSCLK_SOURCE); + Assert(CLK.CTRL == CONFIG_SYSCLK_SOURCE); + } + + if (need_rc2mhz) { +#ifdef CONFIG_OSC_AUTOCAL_RC2MHZ_REF_OSC + osc_enable(CONFIG_OSC_AUTOCAL_RC2MHZ_REF_OSC); + osc_wait_ready(CONFIG_OSC_AUTOCAL_RC2MHZ_REF_OSC); + osc_enable_autocalibration(OSC_ID_RC2MHZ, + CONFIG_OSC_AUTOCAL_RC2MHZ_REF_OSC); +#endif + } else { + osc_disable(OSC_ID_RC2MHZ); + } + +#ifdef CONFIG_RTC_SOURCE + sysclk_rtcsrc_enable(CONFIG_RTC_SOURCE); +#endif +} + +void sysclk_enable_module(enum sysclk_port_id port, + uint8_t id) +{ + irqflags_t flags = cpu_irq_save(); + + *((uint8_t *) & PR.PRGEN + port) &= ~id; + + cpu_irq_restore(flags); +} + +void sysclk_disable_module(enum sysclk_port_id port, + uint8_t id) +{ + irqflags_t flags = cpu_irq_save(); + + *((uint8_t *) & PR.PRGEN + port) |= id; + + cpu_irq_restore(flags); +} + +#if XMEGA_AU || XMEGA_B || XMEGA_C || defined(__DOXYGEN__) + +/** + * \brief Enable clock for the USB module + * + * \pre CONFIG_USBCLK_SOURCE must be defined. + * + * \param frequency The required USB clock frequency in MHz: + * \arg \c 6 for 6 MHz + * \arg \c 48 for 48 MHz + */ +void sysclk_enable_usb(uint8_t frequency) +{ + uint8_t prescaler; + + Assert((frequency == 6) || (frequency == 48)); + + /* + * Enable or disable prescaler depending on if the USB frequency is 6 + * MHz or 48 MHz. Only 6 MHz USB frequency requires prescaling. + */ + if (frequency == 6) { + prescaler = CLK_USBPSDIV_8_gc; + } else { + prescaler = 0; + } + + /* + * Switch to the system clock selected by the user. + */ + switch (CONFIG_USBCLK_SOURCE) { + case USBCLK_SRC_RCOSC: + if (!osc_is_ready(OSC_ID_RC32MHZ)) { + osc_enable(OSC_ID_RC32MHZ); + osc_wait_ready(OSC_ID_RC32MHZ); +#ifdef CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC + if (CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC != OSC_ID_USBSOF) { + osc_enable(CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC); + osc_wait_ready(CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC); + } + osc_enable_autocalibration(OSC_ID_RC32MHZ, + CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC); +#endif + } + ccp_write_io((uint8_t *) & CLK.USBCTRL, (prescaler) + | CLK_USBSRC_RC32M_gc | CLK_USBSEN_bm); + break; + +#ifdef CONFIG_PLL0_SOURCE + case USBCLK_SRC_PLL: + pll_enable_config_defaults(0); + ccp_write_io((uint8_t *) & CLK.USBCTRL, (prescaler) + | CLK_USBSRC_PLL_gc | CLK_USBSEN_bm); + break; +#endif + + default: + Assert(false); + break; + } + + sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_USB); +} + +/** + * \brief Disable clock for the USB module + */ +void sysclk_disable_usb(void) +{ + sysclk_disable_module(SYSCLK_PORT_GEN, SYSCLK_USB); + ccp_write_io((uint8_t *) & CLK.USBCTRL, 0); +} +#endif // XMEGA_AU || XMEGA_B || XMEGA_C diff --git a/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/sysclk.h b/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/sysclk.h new file mode 100644 index 00000000..6ca7eb0b --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/clock/xmega/sysclk.h @@ -0,0 +1,1681 @@ +/** + * \file + * + * \brief Chip-specific system clock management functions + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef XMEGA_SYSCLK_H_INCLUDED +#define XMEGA_SYSCLK_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +// Include clock configuration for the project. +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * \page sysclk_quickstart Quick Start Guide for the System Clock Management service (XMEGA) + * + * This is the quick start guide for the \ref sysclk_group "System Clock Management" + * service, with step-by-step instructions on how to configure and use the service for + * specific use cases. + * + * \section sysclk_quickstart_usecases System Clock Management use cases + * - \ref sysclk_quickstart_basic + * - \ref sysclk_quickstart_use_case_2 + * - \ref sysclk_quickstart_use_case_3 + * + * \section sysclk_quickstart_basic Basic usage of the System Clock Management service + * This section will present a basic use case for the System Clock Management service. + * This use case will configure the main system clock to 32MHz, using an internal PLL + * module to multiply the frequency of a crystal attached to the microcontroller. The + * secondary peripheral bus clock and CPU clock are scaled down from the speed of the + * main system clock. + * + * \subsection sysclk_quickstart_use_case_1_prereq Prerequisites + * - None + * + * \subsection sysclk_quickstart_use_case_1_setup_steps Initialization code + * Add to the application initialization code: + * \code + * sysclk_init(); + * \endcode + * + * \subsection sysclk_quickstart_use_case_1_setup_steps_workflow Workflow + * -# Configure the system clocks according to the settings in conf_clock.h: + * \code sysclk_init(); \endcode + * + * \subsection sysclk_quickstart_use_case_1_example_code Example code + * Add or uncomment the following in your conf_clock.h header file, commenting out all other + * definitions of the same symbol(s): + * \code + * #define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_PLL + * + * // Fpll0 = (Fclk * PLL_mul) / PLL_div + * #define CONFIG_PLL0_SOURCE PLL_SRC_XOSC + * #define CONFIG_PLL0_MUL (32000000UL / BOARD_XOSC_HZ) + * #define CONFIG_PLL0_DIV 1 + * + * // Fbus = Fsys / (2 ^ BUS_div) + * #define CONFIG_SYSCLK_PSADIV SYSCLK_PSADIV_1 + * #define CONFIG_SYSCLK_PSBCDIV SYSCLK_PSBCDIV_1_2 + * \endcode + * + * \subsection sysclk_quickstart_use_case_1_example_workflow Workflow + * -# Configure the main system clock to use the output of the PLL module as its source: + * \code #define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_PLL \endcode + * -# Configure the PLL0 module to use external crystal oscillator XOSC as its source: + * \code #define CONFIG_PLL0_SOURCE PLL_SRC_XOSC \endcode + * -# Configure the PLL0 module to multiply the external oscillator XOSC frequency up to 32MHz: + * \code + * #define CONFIG_PLL0_MUL (32000000UL / BOARD_XOSC_HZ) + * #define CONFIG_PLL0_DIV 1 + * \endcode + * \note For user boards, \c BOARD_XOSC_HZ should be defined in the board \c conf_board.h configuration + * file as the frequency of the crystal attached to XOSC. + * -# Configure the main CPU clock and slow peripheral bus to run at 16MHz, run the fast peripheral bus + * at the full 32MHz speed: + * \code + * #define CONFIG_SYSCLK_PSADIV SYSCLK_PSADIV_1 + * #define CONFIG_SYSCLK_PSBCDIV SYSCLK_PSBCDIV_1_2 + * \endcode + * \note Some dividers are powers of two, while others are integer division factors. Refer to the + * formulas in the conf_clock.h template commented above each division define. + */ + +/** + * \page sysclk_quickstart_use_case_2 Advanced use case - Peripheral Bus Clock Management (XMEGA) + * + * \section sysclk_quickstart_use_case_2 Advanced use case - Peripheral Bus Clock Management + * This section will present a more advanced use case for the System Clock Management service. + * This use case will configure the main system clock to 32MHz, using an internal PLL + * module to multiply the frequency of a crystal attached to the microcontroller. The peripheral bus + * clocks will run at the same speed as the CPU clock, and the USB clock will be configured to use + * the internal 32MHz (nominal) RC oscillator calibrated to 48MHz with the USB Start-of-Frame as the + * calibration reference. + * + * \subsection sysclk_quickstart_use_case_2_prereq Prerequisites + * - None + * + * \subsection sysclk_quickstart_use_case_2_setup_steps Initialization code + * Add to the application initialization code: + * \code + * sysclk_init(); + * \endcode + * + * \subsection sysclk_quickstart_use_case_2_setup_steps_workflow Workflow + * -# Configure the system clocks according to the settings in conf_clock.h: + * \code sysclk_init(); \endcode + * + * \subsection sysclk_quickstart_use_case_2_example_code Example code + * Add or uncomment the following in your conf_clock.h header file, commenting out all other + * definitions of the same symbol(s): + * \code + * #define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_PLL + * + * // Fpll0 = (Fclk * PLL_mul) / PLL_div + * #define CONFIG_PLL0_SOURCE PLL_SRC_XOSC + * #define CONFIG_PLL0_MUL (32000000UL / BOARD_XOSC_HZ) + * #define CONFIG_PLL0_DIV 1 + * + * // Fbus = Fsys / (2 ^ BUS_div) + * #define CONFIG_SYSCLK_PSADIV SYSCLK_PSADIV_1 + * #define CONFIG_SYSCLK_PSBCDIV SYSCLK_PSBCDIV_1_1 + * + * #define CONFIG_USBCLK_SOURCE USBCLK_SRC_RCOSC + * #define CONFIG_OSC_RC32_CAL 48000000UL + * #define CONFIG_OSC_AUTOCAL OSC_ID_RC32MHZ + * #define CONFIG_OSC_AUTOCAL_REF_OSC OSC_ID_USBSOF + * \endcode + * + * \subsection sysclk_quickstart_use_case_2_example_workflow Workflow + * -# Configure the main system clock to use the output of the PLL module as its source: + * \code #define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_PLL \endcode + * -# Configure the PLL0 module to use external crystal oscillator XOSC as its source: + * \code #define CONFIG_PLL0_SOURCE PLL_SRC_XOSC \endcode + * -# Configure the PLL0 module to multiply the external oscillator XOSC frequency up to 32MHz: + * \code + * #define CONFIG_PLL0_MUL (32000000UL / BOARD_XOSC_HZ) + * #define CONFIG_PLL0_DIV 1 + * \endcode + * \note For user boards, \c BOARD_XOSC_HZ should be defined in the board \c conf_board.h configuration + * file as the frequency of the crystal attached to XOSC. + * -# Configure the main CPU and peripheral bus clocks to run at 32MHz: + * \code + * #define CONFIG_SYSCLK_PSADIV SYSCLK_PSADIV_1 + * #define CONFIG_SYSCLK_PSBCDIV SYSCLK_PSBCDIV_1_2 + * \endcode + * \note Some dividers are powers of two, while others are integer division factors. Refer to the + * formulas in the conf_clock.h template commented above each division define. + * -# Configure the USB module clock to use the internal fast (32MHz) RC oscillator: + * \code + * #define CONFIG_USBCLK_SOURCE USBCLK_SRC_RCOSC + * \endcode + * \note When the internal RC oscillator is used for the USB module, it must be recalibrated to 48MHz for + * the USB peripheral to function. If this oscillator is then used as the main system clock source, + * the clock must be divided down via the peripheral and CPU bus clock division constants to ensure + * that the maximum allowable CPU frequency is not exceeded. + * -# Configure the internal fast (32MHz) RC oscillator to calibrate to 48MHz using the USB Start of Frame (SOF) + * as the calibration reference: + * \code + * #define CONFIG_OSC_RC32_CAL 48000000UL + * #define CONFIG_OSC_AUTOCAL OSC_ID_RC32MHZ + * #define CONFIG_OSC_AUTOCAL_REF_OSC OSC_ID_USBSOF + * \endcode + */ + +/** + * \page sysclk_quickstart_use_case_3 Advanced use case - DFLL auto-calibration (XMEGA) + * + * \section sysclk_quickstart_use_case_3 Advanced use case - DFLL auto-calibration + * This section will present a more advanced use case for the System Clock + * Management service. This use case will configure the main system clock to + * 2MHz, using the internal 2MHz RC oscillator calibrated against the internal + * 32KHz oscillator. The peripheral bus clocks will run at the same speed as + * the CPU clock, and the USB clock will be configured to use the internal + * 32MHz (nominal) RC oscillator calibrated to 48MHz with the USB + * Start-of-Frame as the calibration reference. + * + * \subsection sysclk_quickstart_use_case_3_prereq Prerequisites + * - None + * + * \subsection sysclk_quickstart_use_case_3_setup_steps Initialization code + * Add to the application initialization code: + * \code + * sysclk_init(); + * \endcode + * + * \subsection sysclk_quickstart_use_case_3_setup_steps_workflow Workflow + * -# Configure the system clocks according to the settings in conf_clock.h: + * \code sysclk_init(); \endcode + * + * \subsection sysclk_quickstart_use_case_3_example_code Example code + * Add or uncomment the following in your conf_clock.h header file, + * commenting out all other definitions of the same symbol(s): + * \code + * #define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_RC2MHZ + * + * #define CONFIG_OSC_AUTOCAL_RC2MHZ_REF_OSC OSC_ID_RC32KHZ + * + * #define CONFIG_USBCLK_SOURCE USBCLK_SRC_RCOSC + * #define CONFIG_OSC_RC32_CAL 48000000UL + * #define CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC OSC_ID_USBSOF + * \endcode + * + * \subsection sysclk_quickstart_use_case_3_example_workflow Workflow + * -# Configure the main system clock to use the internal 2MHz RC oscillator + * as its source: + * \code + * #define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_RC2MHZ + * \endcode + * -# Configure the 2MHz DFLL auto-calibration to use the internal 32KHz RC + * oscillator: + * \code + * #define CONFIG_OSC_AUTOCAL_RC2MHZ_REF_OSC OSC_ID_RC32KHZ + * \endcode + * \note For auto-calibration it's typically more relevant to use an external + * 32KHz crystal. So if that's the case use OSC_ID_XOSC instead. + * -# Configure the USB module clock to use the internal fast (32MHz) RC oscillator: + * \code + * #define CONFIG_USBCLK_SOURCE USBCLK_SRC_RCOSC + * \endcode + * -# Configure the internal fast (32MHz) RC oscillator to calibrate to 48MHz + * using the USB Start of Frame (SOF) as the calibration reference: + * \code + * #define CONFIG_USBCLK_SOURCE USBCLK_SRC_RCOSC + * #define CONFIG_OSC_RC32_CAL 48000000UL + * #define CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC OSC_ID_USBSOF + * \endcode + */ + +/* Wrap old config into new one */ +#ifdef CONFIG_OSC_AUTOCAL +# if CONFIG_OSC_AUTOCAL == OSC_ID_RC2MHZ +# define CONFIG_OSC_AUTOCAL_RC2MHZ_REF_OSC CONFIG_OSC_AUTOCAL_REF_OSC +# elif CONFIG_OSC_AUTOCAL == OSC_ID_RC32MHZ +# define CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC CONFIG_OSC_AUTOCAL_REF_OSC +# else +# error Bad configuration of CONFIG_OSC_AUTOCAL and/or CONFIG_OSC_AUTOCAL_REF_OSC +# endif +#endif + +// Use 2 MHz with no prescaling if config was empty. +#ifndef CONFIG_SYSCLK_SOURCE +# define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_RC2MHZ +#endif /* CONFIG_SYSCLK_SOURCE */ + +#ifndef CONFIG_SYSCLK_PSADIV +# define CONFIG_SYSCLK_PSADIV SYSCLK_PSADIV_1 +#endif /* CONFIG_SYSCLK_PSADIV */ + +#ifndef CONFIG_SYSCLK_PSBCDIV +# define CONFIG_SYSCLK_PSBCDIV SYSCLK_PSBCDIV_1_1 +#endif /* CONFIG_SYSCLK_PSBCDIV */ + +/** + * \weakgroup sysclk_group + * + * \section sysclk_group_config Configuration Symbols + * + * The following configuration symbols may be used to specify the + * initial system clock configuration. If any of the symbols are not + * set, reasonable defaults will be provided. + * - \b CONFIG_SYSCLK_SOURCE: The initial system clock source. + * - \b CONFIG_SYSCLK_PSADIV: The initial Prescaler A setting. + * - \b CONFIG_SYSCLK_PSBCDIV: The initial Prescaler B setting. + * - \b CONFIG_USBCLK_SOURCE: The initial USB clock source. + * + * @{ + */ + +//! \name System Clock Sources +//@{ +//! Internal 2 MHz RC oscillator +#define SYSCLK_SRC_RC2MHZ CLK_SCLKSEL_RC2M_gc +//! Internal 32 MHz RC oscillator +#define SYSCLK_SRC_RC32MHZ CLK_SCLKSEL_RC32M_gc +//! Internal 32 KHz RC oscillator +#define SYSCLK_SRC_RC32KHZ CLK_SCLKSEL_RC32K_gc +//! External oscillator +#define SYSCLK_SRC_XOSC CLK_SCLKSEL_XOSC_gc +//! Phase-Locked Loop +#define SYSCLK_SRC_PLL CLK_SCLKSEL_PLL_gc +#if XMEGA_E +//! Internal 8 MHz RC oscillator +# define SYSCLK_SRC_RC8MHZ CLK_SCLKSEL_RC8M_gc +#endif +//@} + +//! \name Prescaler A Setting (relative to CLKsys) +//@{ +#define SYSCLK_PSADIV_1 CLK_PSADIV_1_gc //!< Do not prescale +#define SYSCLK_PSADIV_2 CLK_PSADIV_2_gc //!< Prescale CLKper4 by 2 +#define SYSCLK_PSADIV_4 CLK_PSADIV_4_gc //!< Prescale CLKper4 by 4 +#define SYSCLK_PSADIV_8 CLK_PSADIV_8_gc //!< Prescale CLKper4 by 8 +#define SYSCLK_PSADIV_16 CLK_PSADIV_16_gc //!< Prescale CLKper4 by 16 +#define SYSCLK_PSADIV_32 CLK_PSADIV_32_gc //!< Prescale CLKper4 by 32 +#define SYSCLK_PSADIV_64 CLK_PSADIV_64_gc //!< Prescale CLKper4 by 64 +#define SYSCLK_PSADIV_128 CLK_PSADIV_128_gc //!< Prescale CLKper4 by 128 +#define SYSCLK_PSADIV_256 CLK_PSADIV_256_gc //!< Prescale CLKper4 by 256 +#define SYSCLK_PSADIV_512 CLK_PSADIV_512_gc //!< Prescale CLKper4 by 512 + +#if XMEGA_E +# define SYSCLK_PSADIV_6 CLK_PSADIV_6_gc //!< Prescale CLKper4 by 6 +# define SYSCLK_PSADIV_10 CLK_PSADIV_10_gc //!< Prescale CLKper4 by 10 +# define SYSCLK_PSADIV_12 CLK_PSADIV_12_gc //!< Prescale CLKper4 by 12 +# define SYSCLK_PSADIV_24 CLK_PSADIV_24_gc //!< Prescale CLKper4 by 24 +# define SYSCLK_PSADIV_48 CLK_PSADIV_48_gc //!< Prescale CLKper4 by 48 +#endif +//@} + +//! \name Prescaler B and C Setting (relative to CLKper4) +//@{ +//! Do not prescale +#define SYSCLK_PSBCDIV_1_1 CLK_PSBCDIV_1_1_gc +//! Prescale CLKper and CLKcpu by 2 +#define SYSCLK_PSBCDIV_1_2 CLK_PSBCDIV_1_2_gc +//! Prescale CLKper2, CLKper and CLKcpu by 4 +#define SYSCLK_PSBCDIV_4_1 CLK_PSBCDIV_4_1_gc +//! Prescale CLKper2 by 2, CLKper and CLKcpu by 4 +#define SYSCLK_PSBCDIV_2_2 CLK_PSBCDIV_2_2_gc +//@} + +//! \name System Clock Port Numbers + enum sysclk_port_id + { + SYSCLK_PORT_GEN, //!< Devices not associated with a specific port. + SYSCLK_PORT_A, //!< Devices on PORTA + SYSCLK_PORT_B, //!< Devices on PORTB + SYSCLK_PORT_C, //!< Devices on PORTC + SYSCLK_PORT_D, //!< Devices on PORTD + SYSCLK_PORT_E, //!< Devices on PORTE + SYSCLK_PORT_F, //!< Devices on PORTF + }; + +/*! \name Clocks not associated with any port + * + * \note See the datasheet for available modules in the device. + */ +//@{ +#define SYSCLK_DMA PR_DMA_bm //!< DMA Controller +#define SYSCLK_EDMA PR_EDMA_bm //!< EDMA Controller +#define SYSCLK_EVSYS PR_EVSYS_bm //!< Event System +#define SYSCLK_RTC PR_RTC_bm //!< Real-Time Counter +#define SYSCLK_EBI PR_EBI_bm //!< Ext Bus Interface +#define SYSCLK_AES PR_AES_bm //!< AES Module +#define SYSCLK_USB PR_USB_bm //!< USB Module +#define SYSCLK_XCL PR_XCL_bm //!< USB Module +//@} + +/*! \name Clocks on PORTA and PORTB + * + * \note See the datasheet for available modules in the device. + */ +//@{ +#define SYSCLK_AC PR_AC_bm //!< Analog Comparator +#define SYSCLK_ADC PR_ADC_bm //!< A/D Converter +#define SYSCLK_DAC PR_DAC_bm //!< D/A Converter +//@} + +/*! \name Clocks on PORTC, PORTD, PORTE and PORTF + * + * \note See the datasheet for available modules in the device. + */ +//@{ +#define SYSCLK_TC0 PR_TC0_bm //!< Timer/Counter 0 +#define SYSCLK_TC1 PR_TC1_bm //!< Timer/Counter 1 +#define SYSCLK_TC4 PR_TC4_bm //!< Timer/Counter 0 +#define SYSCLK_TC5 PR_TC5_bm //!< Timer/Counter 1 +#define SYSCLK_HIRES PR_HIRES_bm //!< Hi-Res Extension +#define SYSCLK_SPI PR_SPI_bm //!< SPI controller +#define SYSCLK_USART0 PR_USART0_bm //!< USART 0 +#define SYSCLK_USART1 PR_USART1_bm //!< USART 1 +#define SYSCLK_TWI PR_TWI_bm //!< TWI controller +//@} + +/** + * \name RTC clock source identifiers + * + * @{ + */ + +/** 1kHz from internal ULP oscillator. Low precision */ +#define SYSCLK_RTCSRC_ULP CLK_RTCSRC_ULP_gc +/** 1.024kHz from 32.768kHz crystal oscillator TOSC */ +#define SYSCLK_RTCSRC_TOSC CLK_RTCSRC_TOSC_gc +/** 1.024kHz from 32.768kHz internal RC oscillator */ +#define SYSCLK_RTCSRC_RCOSC CLK_RTCSRC_RCOSC_gc +/** 32.768kHz from crystal oscillator TOSC */ +#define SYSCLK_RTCSRC_TOSC32 CLK_RTCSRC_TOSC32_gc +/** 32.768kHz from internal RC oscillator */ +#define SYSCLK_RTCSRC_RCOSC32 CLK_RTCSRC_RCOSC32_gc +/** External clock on TOSC1 */ +#define SYSCLK_RTCSRC_EXTCLK CLK_RTCSRC_EXTCLK_gc + +/** @} */ + +#if XMEGA_AU || XMEGA_B || XMEGA_C +//! \name USB Clock Sources +//@{ +//! Internal 32 MHz RC oscillator +#define USBCLK_SRC_RCOSC 0 +//! Phase-Locked Loop +#define USBCLK_SRC_PLL 1 +//@} + +/** + * \def CONFIG_USBCLK_SOURCE + * \brief Configuration symbol for the USB clock source + * + * If the device features an USB module, and this is intended to be used, this + * symbol must be defined with the clock source configuration. + * + * Define this as one of the \c USBCLK_SRC_xxx definitions. If the PLL is + * selected, it must be configured to run at 48 MHz. If the 32 MHz RC oscillator + * is selected, it must be tuned to 48 MHz by means of the DFLL. + */ +#ifdef __DOXYGEN__ +# define CONFIG_USBCLK_SOURCE +#endif + +#endif // XMEGA_AU || XMEGA_B || XMEGA_C + +#ifndef __ASSEMBLY__ + +/** + * \name Querying the system clock and its derived clocks + */ +//@{ + +/** + * \brief Return the current rate in Hz of the main system clock + * + * \todo This function assumes that the main clock source never changes + * once it's been set up, and that PLL0 always runs at the compile-time + * configured default rate. While this is probably the most common + * configuration, which we want to support as a special case for + * performance reasons, we will at some point need to support more + * dynamic setups as well. + * + * \return Frequency of the main system clock, in Hz. + */ + static inline uint32_t sysclk_get_main_hz (void) + { + switch (CONFIG_SYSCLK_SOURCE) + { + case SYSCLK_SRC_RC2MHZ: + return 2000000UL; +#if XMEGA_E + case SYSCLK_SRC_RC8MHZ:return 8000000UL; +#endif + case SYSCLK_SRC_RC32MHZ: +#ifdef CONFIG_OSC_RC32_CAL + return CONFIG_OSC_RC32_CAL; +#else + return 32000000UL; +#endif + + case SYSCLK_SRC_RC32KHZ:return 32768UL; + +#ifdef BOARD_XOSC_HZ + case SYSCLK_SRC_XOSC:return BOARD_XOSC_HZ; +#endif + +#ifdef CONFIG_PLL0_SOURCE + case SYSCLK_SRC_PLL:return pll_get_default_rate (0); +#endif + + default: + //unhandled_case(CONFIG_SYSCLK_SOURCE); + return 0; + } + } + +/** + * \brief Return the current rate in Hz of clk_PER4. + * + * This clock can run up to four times faster than the CPU clock. + * + * \return Frequency of the clk_PER4 clock, in Hz. + */ + static inline uint32_t sysclk_get_per4_hz (void) + { + uint8_t shift = 0; + +#if XMEGA_E + if (CONFIG_SYSCLK_PSADIV > SYSCLK_PSADIV_512) + { + switch (CONFIG_SYSCLK_PSADIV) + { + case SYSCLK_PSADIV_6: + return sysclk_get_main_hz () / 6; + case SYSCLK_PSADIV_10: + return sysclk_get_main_hz () / 10; + case SYSCLK_PSADIV_12: + return sysclk_get_main_hz () / 12; + case SYSCLK_PSADIV_24: + return sysclk_get_main_hz () / 24; + case SYSCLK_PSADIV_48: + return sysclk_get_main_hz () / 48; + default: + //unhandled_case; + return 0; + } + } +#endif + if (CONFIG_SYSCLK_PSADIV & (1U << CLK_PSADIV_gp)) + { + shift = (CONFIG_SYSCLK_PSADIV >> (1 + CLK_PSADIV_gp)) + 1; + } + + return sysclk_get_main_hz () >> shift; + } + +/** + * \brief Return the current rate in Hz of clk_PER2. + * + * This clock can run up to two times faster than the CPU clock. + * + * \return Frequency of the clk_PER2 clock, in Hz. + */ + static inline uint32_t sysclk_get_per2_hz (void) + { + switch (CONFIG_SYSCLK_PSBCDIV) + { + case SYSCLK_PSBCDIV_1_1: /* Fall through */ + case SYSCLK_PSBCDIV_1_2: + return sysclk_get_per4_hz (); + + case SYSCLK_PSBCDIV_4_1: + return sysclk_get_per4_hz () / 4; + + case SYSCLK_PSBCDIV_2_2: + return sysclk_get_per4_hz () / 2; + + default: + //unhandled_case(CONFIG_SYSCLK_PSBCDIV); + return 0; + } + } + +/** + * \brief Return the current rate in Hz of clk_PER. + * + * This clock always runs at the same rate as the CPU clock unless the divider + * is set. + * + * \return Frequency of the clk_PER clock, in Hz. + */ + static inline uint32_t sysclk_get_per_hz (void) + { + if (CONFIG_SYSCLK_PSBCDIV & (1U << CLK_PSBCDIV_gp)) + return sysclk_get_per2_hz () / 2; + else + return sysclk_get_per2_hz (); + } + +/** + * \brief Return the current rate in Hz of the CPU clock. + * + * \return Frequency of the CPU clock, in Hz. + */ + static inline uint32_t sysclk_get_cpu_hz (void) + { + return sysclk_get_per_hz (); + } + +/** + * \brief Retrieves the current rate in Hz of the Peripheral Bus clock attached + * to the specified peripheral. + * + * \param module Pointer to the module's base address. + * + * \return Frequency of the bus attached to the specified peripheral, in Hz. + */ + static inline uint32_t sysclk_get_peripheral_bus_hz (const volatile void + *module) + { + if (module == NULL) + { + Assert (false); + return 0; + } +#ifdef AES + else if (module == &AES) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef EBI + else if (module == &EBI) + { + return sysclk_get_per2_hz (); + } +#endif +#ifdef RTC + else if (module == &RTC) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef EVSYS + else if (module == &EVSYS) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef DMA + else if (module == &DMA) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef EDMA + else if (module == &EDMA) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef ACA + else if (module == &ACA) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef ACB + else if (module == &ACB) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef ADCA + else if (module == &ADCA) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef ADCB + else if (module == &ADCB) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef DACA + else if (module == &DACA) + { + return sysclk_get_per_hz (); + } +#endif +// Workaround for bad XMEGA D header file +#if !XMEGA_D +#ifdef DACB + else if (module == &DACB) + { + return sysclk_get_per_hz (); + } +#endif +#endif // Workaround end +#ifdef FAULTC0 + else if (module == &FAULTC0) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef FAULTC1 + else if (module == &FAULTC1) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCC0 + else if (module == &TCC0) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCD0 + else if (module == &TCD0) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCE0 + else if (module == &TCE0) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCF0 + else if (module == &TCF0) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCC1 + else if (module == &TCC1) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCD1 + else if (module == &TCD1) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCE1 + else if (module == &TCE1) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCF1 + else if (module == &TCF1) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCC4 + else if (module == &TCC4) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCC5 + else if (module == &TCC5) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCD4 + else if (module == &TCD4) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TCD5 + else if (module == &TCD5) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef HIRESC + else if (module == &HIRESC) + { + return sysclk_get_per4_hz (); + } +#endif +#ifdef HIRESD + else if (module == &HIRESD) + { + return sysclk_get_per4_hz (); + } +#endif +#ifdef HIRESE + else if (module == &HIRESE) + { + return sysclk_get_per4_hz (); + } +#endif +#ifdef HIRESF + else if (module == &HIRESF) + { + return sysclk_get_per4_hz (); + } +#endif +#ifdef SPIC + else if (module == &SPIC) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef SPID + else if (module == &SPID) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef SPIE + else if (module == &SPIE) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef SPIF + else if (module == &SPIF) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef USARTC0 + else if (module == &USARTC0) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef USARTD0 + else if (module == &USARTD0) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef USARTE0 + else if (module == &USARTE0) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef USARTF0 + else if (module == &USARTF0) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef USARTC1 + else if (module == &USARTC1) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef USARTD1 + else if (module == &USARTD1) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef USARTE1 + else if (module == &USARTE1) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef USARTF1 + else if (module == &USARTF1) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TWIC + else if (module == &TWIC) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TWID + else if (module == &TWID) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TWIE + else if (module == &TWIE) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef TWIF + else if (module == &TWIF) + { + return sysclk_get_per_hz (); + } +#endif +#ifdef XCL + else if (module == &XCL) + { + return sysclk_get_per_hz (); + } +#endif + else + { + Assert (false); + return 0; + } + } + +//@} + +//! \name Enabling and disabling synchronous clocks +//@{ + +/** + * \brief Enable the clock to peripheral \a id on port \a port + * + * \param port ID of the port to which the module is connected (one of + * the \c SYSCLK_PORT_* definitions). + * \param id The ID (bitmask) of the peripheral module to be enabled. + */ + extern void sysclk_enable_module (enum sysclk_port_id port, uint8_t id); + +/** + * \brief Disable the clock to peripheral \a id on port \a port + * + * \param port ID of the port to which the module is connected (one of + * the \c SYSCLK_PORT_* definitions). + * \param id The ID (bitmask) of the peripheral module to be disabled. + */ + extern void sysclk_disable_module (enum sysclk_port_id port, uint8_t id); + +/** + * \brief Enable a peripheral's clock from its base address. + * + * Enables the clock to a peripheral, given its base address. If the peripheral + * has an associated clock on the HSB bus, this will be enabled also. + * + * \param module Pointer to the module's base address. + */ + static inline void sysclk_enable_peripheral_clock (const volatile void + *module) + { + if (module == NULL) + { + Assert (false); + } +#ifdef AES + else if (module == &AES) + { + sysclk_enable_module (SYSCLK_PORT_GEN, SYSCLK_AES); + } +#endif +#ifdef EBI + else if (module == &EBI) + { + sysclk_enable_module (SYSCLK_PORT_GEN, SYSCLK_EBI); + } +#endif +#ifdef RTC + else if (module == &RTC) + { + sysclk_enable_module (SYSCLK_PORT_GEN, SYSCLK_RTC); + } +#endif +#ifdef EVSYS + else if (module == &EVSYS) + { + sysclk_enable_module (SYSCLK_PORT_GEN, SYSCLK_EVSYS); + } +#endif +#ifdef DMA + else if (module == &DMA) + { + sysclk_enable_module (SYSCLK_PORT_GEN, SYSCLK_DMA); + } +#endif +#ifdef EDMA + else if (module == &EDMA) + { + sysclk_enable_module (SYSCLK_PORT_GEN, SYSCLK_EDMA); + } +#endif +#ifdef ACA + else if (module == &ACA) + { + sysclk_enable_module (SYSCLK_PORT_A, SYSCLK_AC); + } +#endif +#ifdef ACB + else if (module == &ACB) + { + sysclk_enable_module (SYSCLK_PORT_B, SYSCLK_AC); + } +#endif +#ifdef ADCA + else if (module == &ADCA) + { + sysclk_enable_module (SYSCLK_PORT_A, SYSCLK_ADC); + } +#endif +#ifdef ADCB + else if (module == &ADCB) + { + sysclk_enable_module (SYSCLK_PORT_B, SYSCLK_ADC); + } +#endif +#ifdef DACA + else if (module == &DACA) + { + sysclk_enable_module (SYSCLK_PORT_A, SYSCLK_DAC); + } +#endif +// Workaround for bad XMEGA D header file +#if !XMEGA_D +#ifdef DACB + else if (module == &DACB) + { + sysclk_enable_module (SYSCLK_PORT_B, SYSCLK_DAC); + } +#endif +#endif // Workaround end +#ifdef TCC0 + else if (module == &TCC0) + { + sysclk_enable_module (SYSCLK_PORT_C, SYSCLK_TC0); + } +#endif +#ifdef TCD0 + else if (module == &TCD0) + { + sysclk_enable_module (SYSCLK_PORT_D, SYSCLK_TC0); + } +#endif +#ifdef TCE0 + else if (module == &TCE0) + { + sysclk_enable_module (SYSCLK_PORT_E, SYSCLK_TC0); + } +#endif +#ifdef TCF0 + else if (module == &TCF0) + { + sysclk_enable_module (SYSCLK_PORT_F, SYSCLK_TC0); + } +#endif +#ifdef TCC1 + else if (module == &TCC1) + { + sysclk_enable_module (SYSCLK_PORT_C, SYSCLK_TC1); + } +#endif +#ifdef TCD1 + else if (module == &TCD1) + { + sysclk_enable_module (SYSCLK_PORT_D, SYSCLK_TC1); + } +#endif +#ifdef TCE1 + else if (module == &TCE1) + { + sysclk_enable_module (SYSCLK_PORT_E, SYSCLK_TC1); + } +#endif +#ifdef TCF1 + else if (module == &TCF1) + { + sysclk_enable_module (SYSCLK_PORT_F, SYSCLK_TC1); + } +#endif +#ifdef TCC4 + else if (module == &TCC4) + { + sysclk_enable_module (SYSCLK_PORT_C, SYSCLK_TC4); + } +#endif +#ifdef TCC5 + else if (module == &TCC5) + { + sysclk_enable_module (SYSCLK_PORT_C, SYSCLK_TC5); + } +#endif +#ifdef TCD4 + else if (module == &TCD4) + { + sysclk_enable_module (SYSCLK_PORT_D, SYSCLK_TC4); + } +#endif +#ifdef TCD5 + else if (module == &TCD5) + { + sysclk_enable_module (SYSCLK_PORT_D, SYSCLK_TC5); + } +#endif +#ifdef HIRESC + else if (module == &HIRESC) + { + sysclk_enable_module (SYSCLK_PORT_C, SYSCLK_HIRES); + } +#endif +#ifdef HIRESD + else if (module == &HIRESD) + { + sysclk_enable_module (SYSCLK_PORT_D, SYSCLK_HIRES); + } +#endif +#ifdef HIRESE + else if (module == &HIRESE) + { + sysclk_enable_module (SYSCLK_PORT_E, SYSCLK_HIRES); + } +#endif +#ifdef HIRESF + else if (module == &HIRESF) + { + sysclk_enable_module (SYSCLK_PORT_F, SYSCLK_HIRES); + } +#endif +#ifdef SPIC + else if (module == &SPIC) + { + sysclk_enable_module (SYSCLK_PORT_C, SYSCLK_SPI); + } +#endif +#ifdef SPID + else if (module == &SPID) + { + sysclk_enable_module (SYSCLK_PORT_D, SYSCLK_SPI); + } +#endif +#ifdef SPIE + else if (module == &SPIE) + { + sysclk_enable_module (SYSCLK_PORT_E, SYSCLK_SPI); + } +#endif +#ifdef SPIF + else if (module == &SPIF) + { + sysclk_enable_module (SYSCLK_PORT_F, SYSCLK_SPI); + } +#endif +#ifdef USARTC0 + else if (module == &USARTC0) + { + sysclk_enable_module (SYSCLK_PORT_C, SYSCLK_USART0); + } +#endif +#ifdef USARTD0 + else if (module == &USARTD0) + { + sysclk_enable_module (SYSCLK_PORT_D, SYSCLK_USART0); + } +#endif +#ifdef USARTE0 + else if (module == &USARTE0) + { + sysclk_enable_module (SYSCLK_PORT_E, SYSCLK_USART0); + } +#endif +#ifdef USARTF0 + else if (module == &USARTF0) + { + sysclk_enable_module (SYSCLK_PORT_F, SYSCLK_USART0); + } +#endif +#ifdef USARTC1 + else if (module == &USARTC1) + { + sysclk_enable_module (SYSCLK_PORT_C, SYSCLK_USART1); + } +#endif +#ifdef USARTD1 + else if (module == &USARTD1) + { + sysclk_enable_module (SYSCLK_PORT_D, SYSCLK_USART1); + } +#endif +#ifdef USARTE1 + else if (module == &USARTE1) + { + sysclk_enable_module (SYSCLK_PORT_E, SYSCLK_USART1); + } +#endif +#ifdef USARTF1 + else if (module == &USARTF1) + { + sysclk_enable_module (SYSCLK_PORT_F, SYSCLK_USART1); + } +#endif +#ifdef TWIC + else if (module == &TWIC) + { + sysclk_enable_module (SYSCLK_PORT_C, SYSCLK_TWI); + } +#endif +#ifdef TWID + else if (module == &TWID) + { + sysclk_enable_module (SYSCLK_PORT_D, SYSCLK_TWI); + } +#endif +#ifdef TWIE + else if (module == &TWIE) + { + sysclk_enable_module (SYSCLK_PORT_E, SYSCLK_TWI); + } +#endif +#ifdef TWIF + else if (module == &TWIF) + { + sysclk_enable_module (SYSCLK_PORT_F, SYSCLK_TWI); + } +#endif +#ifdef XCL + else if (module == &XCL) + { + sysclk_enable_module (SYSCLK_PORT_GEN, SYSCLK_XCL); + } +#endif + else + { + Assert (false); + } + } + +/** + * \brief Disable a peripheral's clock from its base address. + * + * Disables the clock to a peripheral, given its base address. If the peripheral + * has an associated clock on the HSB bus, this will be disabled also. + * + * \param module Pointer to the module's base address. + */ + static inline void sysclk_disable_peripheral_clock (const volatile void + *module) + { + if (module == NULL) + { + Assert (false); + } +#ifdef AES + else if (module == &AES) + { + sysclk_disable_module (SYSCLK_PORT_GEN, SYSCLK_AES); + } +#endif +#ifdef EBI + else if (module == &EBI) + { + sysclk_disable_module (SYSCLK_PORT_GEN, SYSCLK_EBI); + } +#endif +#ifdef RTC + else if (module == &RTC) + { + sysclk_disable_module (SYSCLK_PORT_GEN, SYSCLK_RTC); + } +#endif +#ifdef EVSYS + else if (module == &EVSYS) + { + sysclk_disable_module (SYSCLK_PORT_GEN, SYSCLK_EVSYS); + } +#endif +#ifdef DMA + else if (module == &DMA) + { + sysclk_disable_module (SYSCLK_PORT_GEN, SYSCLK_DMA); + } +#endif +#ifdef EDMA + else if (module == &EDMA) + { + sysclk_disable_module (SYSCLK_PORT_GEN, SYSCLK_EDMA); + } +#endif +#ifdef ACA + else if (module == &ACA) + { + sysclk_disable_module (SYSCLK_PORT_A, SYSCLK_AC); + } +#endif +#ifdef ACB + else if (module == &ACB) + { + sysclk_disable_module (SYSCLK_PORT_B, SYSCLK_AC); + } +#endif +#ifdef ADCA + else if (module == &ADCA) + { + sysclk_disable_module (SYSCLK_PORT_A, SYSCLK_ADC); + } +#endif +#ifdef ADCB + else if (module == &ADCB) + { + sysclk_disable_module (SYSCLK_PORT_B, SYSCLK_ADC); + } +#endif +#ifdef DACA + else if (module == &DACA) + { + sysclk_disable_module (SYSCLK_PORT_A, SYSCLK_DAC); + } +#endif +// Workaround for bad XMEGA D header file +#if !XMEGA_D +#ifdef DACB + else if (module == &DACB) + { + sysclk_disable_module (SYSCLK_PORT_B, SYSCLK_DAC); + } +#endif +#endif // Workaround end +#ifdef TCC0 + else if (module == &TCC0) + { + sysclk_disable_module (SYSCLK_PORT_C, SYSCLK_TC0); + } +#endif +#ifdef TCD0 + else if (module == &TCD0) + { + sysclk_disable_module (SYSCLK_PORT_D, SYSCLK_TC0); + } +#endif +#ifdef TCE0 + else if (module == &TCE0) + { + sysclk_disable_module (SYSCLK_PORT_E, SYSCLK_TC0); + } +#endif +#ifdef TCF0 + else if (module == &TCF0) + { + sysclk_disable_module (SYSCLK_PORT_F, SYSCLK_TC0); + } +#endif +#ifdef TCC1 + else if (module == &TCC1) + { + sysclk_disable_module (SYSCLK_PORT_C, SYSCLK_TC1); + } +#endif +#ifdef TCD1 + else if (module == &TCD1) + { + sysclk_disable_module (SYSCLK_PORT_D, SYSCLK_TC1); + } +#endif +#ifdef TCE1 + else if (module == &TCE1) + { + sysclk_disable_module (SYSCLK_PORT_E, SYSCLK_TC1); + } +#endif +#ifdef TCF1 + else if (module == &TCF1) + { + sysclk_disable_module (SYSCLK_PORT_F, SYSCLK_TC1); + } +#endif +#ifdef TCC4 + else if (module == &TCC4) + { + sysclk_disable_module (SYSCLK_PORT_C, SYSCLK_TC4); + } +#endif +#ifdef TCC5 + else if (module == &TCC5) + { + sysclk_disable_module (SYSCLK_PORT_C, SYSCLK_TC5); + } +#endif +#ifdef TCD4 + else if (module == &TCD4) + { + sysclk_disable_module (SYSCLK_PORT_D, SYSCLK_TC4); + } +#endif +#ifdef TCD5 + else if (module == &TCD5) + { + sysclk_disable_module (SYSCLK_PORT_D, SYSCLK_TC5); + } +#endif +#ifdef HIRESC + else if (module == &HIRESC) + { + sysclk_disable_module (SYSCLK_PORT_C, SYSCLK_HIRES); + } +#endif +#ifdef HIRESD + else if (module == &HIRESD) + { + sysclk_disable_module (SYSCLK_PORT_D, SYSCLK_HIRES); + } +#endif +#ifdef HIRESE + else if (module == &HIRESE) + { + sysclk_disable_module (SYSCLK_PORT_E, SYSCLK_HIRES); + } +#endif +#ifdef HIRESF + else if (module == &HIRESF) + { + sysclk_disable_module (SYSCLK_PORT_F, SYSCLK_HIRES); + } +#endif +#ifdef SPIC + else if (module == &SPIC) + { + sysclk_disable_module (SYSCLK_PORT_C, SYSCLK_SPI); + } +#endif +#ifdef SPID + else if (module == &SPID) + { + sysclk_disable_module (SYSCLK_PORT_D, SYSCLK_SPI); + } +#endif +#ifdef SPIE + else if (module == &SPIE) + { + sysclk_disable_module (SYSCLK_PORT_E, SYSCLK_SPI); + } +#endif +#ifdef SPIF + else if (module == &SPIF) + { + sysclk_disable_module (SYSCLK_PORT_F, SYSCLK_SPI); + } +#endif +#ifdef USARTC0 + else if (module == &USARTC0) + { + sysclk_disable_module (SYSCLK_PORT_C, SYSCLK_USART0); + } +#endif +#ifdef USARTD0 + else if (module == &USARTD0) + { + sysclk_disable_module (SYSCLK_PORT_D, SYSCLK_USART0); + } +#endif +#ifdef USARTE0 + else if (module == &USARTE0) + { + sysclk_disable_module (SYSCLK_PORT_E, SYSCLK_USART0); + } +#endif +#ifdef USARTF0 + else if (module == &USARTF0) + { + sysclk_disable_module (SYSCLK_PORT_F, SYSCLK_USART0); + } +#endif +#ifdef USARTC1 + else if (module == &USARTC1) + { + sysclk_disable_module (SYSCLK_PORT_C, SYSCLK_USART1); + } +#endif +#ifdef USARTD1 + else if (module == &USARTD1) + { + sysclk_disable_module (SYSCLK_PORT_D, SYSCLK_USART1); + } +#endif +#ifdef USARTE1 + else if (module == &USARTE1) + { + sysclk_disable_module (SYSCLK_PORT_E, SYSCLK_USART1); + } +#endif +#ifdef USARTF1 + else if (module == &USARTF1) + { + sysclk_disable_module (SYSCLK_PORT_F, SYSCLK_USART1); + } +#endif +#ifdef TWIC + else if (module == &TWIC) + { + sysclk_disable_module (SYSCLK_PORT_C, SYSCLK_TWI); + } +#endif +#ifdef TWID + else if (module == &TWID) + { + sysclk_disable_module (SYSCLK_PORT_D, SYSCLK_TWI); + } +#endif +#ifdef TWIE + else if (module == &TWIE) + { + sysclk_disable_module (SYSCLK_PORT_E, SYSCLK_TWI); + } +#endif +#ifdef TWIF + else if (module == &TWIF) + { + sysclk_disable_module (SYSCLK_PORT_F, SYSCLK_TWI); + } +#endif +#ifdef XCL + else if (module == &XCL) + { + sysclk_disable_module (SYSCLK_PORT_GEN, SYSCLK_XCL); + } +#endif + else + { + Assert (false); + } + } + +/** + * \brief Check if the synchronous clock is enabled for a module + * + * \param port ID of the port to which the module is connected (one of + * the \c SYSCLK_PORT_* definitions). + * \param id The ID (bitmask) of the peripheral module to check (one of + * the \c SYSCLK_* module definitions). + * + * \retval true If the clock for module \a id on \a port is enabled. + * \retval false If the clock for module \a id on \a port is disabled. + */ + static inline bool sysclk_module_is_enabled (enum sysclk_port_id port, + uint8_t id) + { + uint8_t mask = *((uint8_t *) & PR.PRGEN + port); + return (mask & id) == 0; + } + +#if XMEGA_AU || XMEGA_B || XMEGA_C || defined(__DOXYGEN__) +# if defined(CONFIG_USBCLK_SOURCE) || defined(__DOXYGEN__) +# if (CONFIG_USBCLK_SOURCE == USBCLK_SRC_RCOSC) +# define USBCLK_STARTUP_TIMEOUT 1 +# elif (CONFIG_USBCLK_SOURCE == USBCLK_SRC_PLL) +# if (CONFIG_PLL0_SOURCE == PLL_SRC_XOSC) +# define USBCLK_STARTUP_TIMEOUT XOSC_STARTUP_TIMEOUT +# elif (CONFIG_PLL0_SOURCE == PLL_SRC_RC32MHZ) +# define USBCLK_STARTUP_TIMEOUT 1 +# elif (CONFIG_PLL0_SOURCE == PLL_SRC_RC2MHZ) +# define USBCLK_STARTUP_TIMEOUT 1 +# else +# error Unknow value for CONFIG_PLL0_SOURCE, see conf_clock.h. +# endif +# endif +# else /* CONFIG_USBCLK_SOURCE not defined */ +# define CONFIG_USBCLK_SOURCE USBCLK_SRC_RCOSC +# define USBCLK_STARTUP_TIMEOUT 1 +# endif /* CONFIG_USBCLK_SOURCE */ + void sysclk_enable_usb (uint8_t frequency); + void sysclk_disable_usb (void); +#endif /* XMEGA_AU || XMEGA_B || XMEGA_C */ +//@} + +//! \name System Clock Source and Prescaler configuration +//@{ + +/** + * \brief Set system clock prescaler configuration + * + * This function will change the system clock prescaler configuration to + * match the parameters. + * + * \note The parameters to this function are device-specific. + * + * \param psadiv The prescaler A setting (one of the \c SYSCLK_PSADIV_* + * definitions). This determines the clkPER4 frequency. + * \param psbcdiv The prescaler B and C settings (one of the \c SYSCLK_PSBCDIV_* + * definitions). These determine the clkPER2, clkPER and clkCPU frequencies. + */ + static inline void sysclk_set_prescalers (uint8_t psadiv, uint8_t psbcdiv) + { + ccp_write_io ((uint8_t *) & CLK.PSCTRL, psadiv | psbcdiv); + } + +/** + * \brief Change the source of the main system clock. + * + * \param src The new system clock source. Must be one of the constants + * from the System Clock Sources section. + */ + static inline void sysclk_set_source (uint8_t src) + { + ccp_write_io ((uint8_t *) & CLK.CTRL, src); + } + +/** + * \brief Lock the system clock configuration + * + * This function will lock the current system clock source and prescaler + * configuration, preventing any further changes. + */ + static inline void sysclk_lock (void) + { + ccp_write_io ((uint8_t *) & CLK.LOCK, CLK_LOCK_bm); + } + +//@} + +/** + * \name RTC clock source control + * @{ + */ + +/** + * \brief Enable RTC clock with specified clock source + * + * \param id RTC clock source ID. Select from SYSCLK_RTCSRC_ULP, + * SYSCLK_RTCSRC_RCOSC, SYSCLK_RTCSRC_TOSC, SYSCLK_RTCSRC_RCOSC32, + * SYSCLK_RTCSRC_TOSC32 or SYSCLK_RTCSRC_EXTCLK + */ + static inline void sysclk_rtcsrc_enable (uint8_t id) + { + Assert ((id & ~CLK_RTCSRC_gm) == 0); + + switch (id) + { + case SYSCLK_RTCSRC_RCOSC: +#if !XMEGA_A && !XMEGA_D + case SYSCLK_RTCSRC_RCOSC32: +#endif + osc_enable (OSC_ID_RC32KHZ); + osc_wait_ready (OSC_ID_RC32KHZ); + break; + case SYSCLK_RTCSRC_TOSC: + case SYSCLK_RTCSRC_TOSC32: +#if !XMEGA_A && !XMEGA_D + case SYSCLK_RTCSRC_EXTCLK: +#endif + osc_enable (OSC_ID_XOSC); + osc_wait_ready (OSC_ID_XOSC); + break; + } + + CLK.RTCCTRL = id | CLK_RTCEN_bm; + } + +/** + * \brief Disable RTC clock + */ + static inline void sysclk_rtcsrc_disable (void) + { + CLK.RTCCTRL = 0; + } + +/** @} */ + +//! \name System Clock Initialization +//@{ + + extern void sysclk_init (void); + +//@} + +#endif /* !__ASSEMBLY__ */ + +//! @} + +#ifdef __cplusplus +} +#endif + +#endif /* XMEGA_SYSCLK_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/delay/delay.h b/bacnet-stack/ports/xplained/ASF/common/services/delay/delay.h new file mode 100644 index 00000000..f92a2413 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/delay/delay.h @@ -0,0 +1,137 @@ +/** + * \file + * + * \brief Common Delay Service + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _DELAY_H_ +#define _DELAY_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#if UC3 +# include +#elif XMEGA +# include "xmega/cycle_counter.h" +#elif MEGA +# include "mega/cycle_counter.h" +#elif SAM +# include "sam/cycle_counter.h" +#endif + +/** + * @defgroup group_common_services_delay Busy-Wait Delay Routines + * + * This module provides simple loop-based delay routines for those + * applications requiring a brief wait during execution. Common API + * for UC3, XMEGA, and AVR MEGA. + * + * @{ + */ + +/** + * @def F_CPU + * @brief MCU Clock Frequency (Hertz) + * + * @deprecated + * The \ref F_CPU configuration constant is used for compatibility with the + * \ref group_common_services_delay routines. The common loop-based delay + * routines are designed to use the \ref clk_group modules while anticipating + * support for legacy applications assuming a statically defined clock + * frequency. Applications using a statically configured MCU clock frequency + * can define \ref F_CPU (Hertz), in which case the common delay routines will + * use this value rather than calling sysclk_get_cpu_hz() to get the current + * MCU clock frequency. + */ +#ifndef F_CPU +# define F_CPU sysclk_get_cpu_hz() +#endif + +/** + * @def delay_init + * + * @brief Initialize the delay driver. + * @param fcpu_hz CPU frequency in Hz + * + * @deprecated + * This function is provided for compatibility with ASF applications that + * may not have been updated to configure the system clock via the common + * clock service; e.g. sysclk_init() and a configuration header file are + * used to configure clocks. + * + * The functions in this module call \ref sysclk_get_cpu_hz() function to + * obtain the system clock frequency. + */ +#define delay_init(fcpu_hz) + +/** + * @def delay_s + * @brief Delay in seconds. + * @param delay Delay in seconds + */ +#define delay_s(delay) cpu_delay_ms(1000 * delay, F_CPU) + +/** + * @def delay_ms + * @brief Delay in milliseconds. + * @param delay Delay in milliseconds + */ +#define delay_ms(delay) cpu_delay_ms(delay, F_CPU) + +/** + * @def delay_us + * @brief Delay in microseconds. + * @param delay Delay in microseconds + */ +#define delay_us(delay) cpu_delay_us(delay, F_CPU) + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* _DELAY_H_ */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/delay/xmega/cycle_counter.h b/bacnet-stack/ports/xplained/ASF/common/services/delay/xmega/cycle_counter.h new file mode 100644 index 00000000..5f116b1c --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/delay/xmega/cycle_counter.h @@ -0,0 +1,118 @@ +/** + * \file + * + * \brief AVR functions for busy-wait delay loops + * + * Copyright (c) 2011 - 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _CYCLE_COUNTER_H_ +#define _CYCLE_COUNTER_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#include + +/** + * @name Convenience functions for busy-wait delay loops + * + * @def delay_cycles + * @brief Delay program execution for a specified number of CPU cycles. + * @param n number of CPU cycles to wait + * + * @def cpu_delay_ms + * @brief Delay program execution for a specified number of milliseconds. + * @param delay number of milliseconds to wait + * @param f_cpu CPU frequency in Hertz + * + * @def cpu_delay_us + * @brief Delay program execution for a specified number of microseconds. + * @param delay number of microseconds to wait + * @param f_cpu CPU frequency in Hertz + * + * @def cpu_ms_2_cy + * @brief Convert milli-seconds into CPU cycles. + * @param ms number of milliseconds + * @param f_cpu CPU frequency in Hertz + * @return the converted number of CPU cycles + * + * @def cpu_us_2_cy + * @brief Convert micro-seconds into CPU cycles. + * @param ms number of microseconds + * @param f_cpu CPU frequency in Hertz + * @return the converted number of CPU cycles + * + * @{ + */ + __always_optimize + static inline void __portable_avr_delay_cycles (unsigned long n) + { + do + { + barrier (); + } + while (--n); + } + +#if !defined(__DELAY_CYCLE_INTRINSICS__) +# define delay_cycles __portable_avr_delay_cycles +# define cpu_ms_2_cy(ms, f_cpu) (((uint64_t)(ms) * (f_cpu) + 999) / 6e3) +# define cpu_us_2_cy(us, f_cpu) (((uint64_t)(us) * (f_cpu) + 999999ul) / 6e6) +#else +# if defined(__GNUC__) +# define delay_cycles __builtin_avr_delay_cycles +# elif defined(__ICCAVR__) +# define delay_cycles __delay_cycles +# endif +# define cpu_ms_2_cy(ms, f_cpu) (((uint64_t)(ms) * (f_cpu) + 999) / 1e3) +# define cpu_us_2_cy(us, f_cpu) (((uint64_t)(us) * (f_cpu) + 999999ul) / 1e6) +#endif + +#define cpu_delay_ms(delay, f_cpu) delay_cycles((uint64_t)cpu_ms_2_cy(delay, f_cpu)) +#define cpu_delay_us(delay, f_cpu) delay_cycles((uint64_t)cpu_us_2_cy(delay, f_cpu)) +//! @} + + +#ifdef __cplusplus +} +#endif + +#endif /* _CYCLE_COUNTER_H_ */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/gpio/gpio.h b/bacnet-stack/ports/xplained/ASF/common/services/gpio/gpio.h new file mode 100644 index 00000000..cb0671c5 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/gpio/gpio.h @@ -0,0 +1,83 @@ +/** + * \file + * + * \brief Common GPIO API. + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _GPIO_H_ +#define _GPIO_H_ + +#include + +#if (SAM3S || SAM3U || SAM3N || SAM3XA || SAM4S || SAM4E) +# include "sam_gpio/sam_gpio.h" +#elif XMEGA +# include "xmega_gpio/xmega_gpio.h" +#elif MEGA || MEGA_RF +# include "mega_gpio/mega_gpio.h" +#else +# error Unsupported chip type +#endif + +/** + * \defgroup gpio_group General Purpose Input/Output + * + * This is the common API for GPIO. Additional features are available + * in the documentation of the specific modules. + * + * \section io_group_platform Platform Dependencies + * + * The following functions are available on all platforms, but there may + * be variations in the function signature (i.e. parameters) and + * behaviour. These functions are typically called by platform-specific + * parts of drivers, and applications that aren't intended to be + * portable: + * - gpio_pin_is_low() + * - gpio_pin_is_high() + * - gpio_set_pin_high() + * - gpio_set_pin_group_high() + * - gpio_set_pin_low() + * - gpio_set_pin_group_low() + * - gpio_toggle_pin() + * - gpio_toggle_pin_group() + * - gpio_configure_pin() + * - gpio_configure_group() + */ + +#endif /* _GPIO_H_ */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/gpio/xmega_gpio/xmega_gpio.h b/bacnet-stack/ports/xplained/ASF/common/services/gpio/xmega_gpio/xmega_gpio.h new file mode 100644 index 00000000..afbd1eef --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/gpio/xmega_gpio/xmega_gpio.h @@ -0,0 +1,80 @@ +/** + * \file + * + * \brief Common gpio data/structure for all AVR XMEGA implementations. + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#ifndef _XMEGA_GPIO_H_ +#define _XMEGA_GPIO_H_ + +#include "compiler.h" +#include "ioport.h" + +#define gpio_pin_is_low(io_id) \ + ioport_pin_is_low(io_id) + +#define gpio_pin_is_high(io_id) \ + ioport_pin_is_high(io_id) + +#define gpio_set_pin_high(io_id) \ + ioport_set_value(io_id,1) + +#define gpio_set_pin_low(io_id) \ + ioport_set_value(io_id,0) + +#define gpio_toggle_pin(io_id) \ + ioport_toggle_pin(io_id) + +#define gpio_configure_pin(io_id,io_flags) \ + ioport_configure_pin(io_id,io_flags) + +#define gpio_configure_group(port_id,port_mask,io_flags) \ + ioport_configure_group(port_id,port_mask,io_flags) + +#define gpio_set_pin_group_high(port_id,mask) \ + ioport_set_group_high(port_id,mask) + +#define gpio_set_pin_group_low(port_id,mask) \ + ioport_set_group_low(port_id,mask) + +#define gpio_toggle_pin_group(port_id,mask) \ + ioport_tgl_group(port_id,mask) + +#endif // _XMEGA_GPIO_H_ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/ioport/ioport.h b/bacnet-stack/ports/xplained/ASF/common/services/ioport/ioport.h new file mode 100644 index 00000000..47843b4d --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/ioport/ioport.h @@ -0,0 +1,547 @@ +/** + * \file + * + * \brief Common IOPORT service main header file for AVR, UC3 and ARM + * architectures. + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef IOPORT_H +#define IOPORT_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +/** + * \defgroup ioport_group Common IOPORT API + * + * See \ref ioport_quickstart. + * + * This is common IOPORT service for GPIO pin configuration and control in a + * standardized manner across the MEGA, MEGA_RF, XMEGA, UC3 and ARM devices. + * + * Port pin control code is optimized for each platform, and should produce + * both compact and fast execution times when used with constant values. + * + * \section dependencies Dependencies + * This driver depends on the following modules: + * - \ref sysclk_group for clock speed and functions. + * @{ + */ + +/** + * \def IOPORT_CREATE_PIN(port, pin) + * \brief Create IOPORT pin number + * + * Create a IOPORT pin number for use with the IOPORT functions. + * + * \param port IOPORT port (e.g. PORTA, PA or PIOA depending on chosen + * architecture) + * \param pin IOPORT zero-based index of the I/O pin + */ + +/** \brief IOPORT pin directions */ + enum ioport_direction + { + IOPORT_DIR_INPUT, /*!< IOPORT input direction */ + IOPORT_DIR_OUTPUT, /*!< IOPORT output direction */ + }; + +/** \brief IOPORT levels */ + enum ioport_value + { + IOPORT_PIN_LEVEL_LOW, /*!< IOPORT pin value low */ + IOPORT_PIN_LEVEL_HIGH, /*!< IOPORT pin value high */ + }; + +#if MEGA_RF +/** \brief IOPORT edge sense modes */ + enum ioport_sense + { + IOPORT_SENSE_LEVEL, /*!< IOPORT sense low level */ + IOPORT_SENSE_BOTHEDGES, /*!< IOPORT sense both rising and falling edges */ + IOPORT_SENSE_FALLING, /*!< IOPORT sense falling edges */ + IOPORT_SENSE_RISING, /*!< IOPORT sense rising edges */ + }; +#elif SAM && !SAM4L +/** \brief IOPORT edge sense modes */ + enum ioport_sense + { + IOPORT_SENSE_BOTHEDGES, /*!< IOPORT sense both rising and falling edges */ + IOPORT_SENSE_FALLING, /*!< IOPORT sense falling edges */ + IOPORT_SENSE_RISING, /*!< IOPORT sense rising edges */ + IOPORT_SENSE_LEVEL_LOW, /*!< IOPORT sense low level */ + IOPORT_SENSE_LEVEL_HIGH, /*!< IOPORT sense High level */ + }; +#else + enum ioport_sense + { + IOPORT_SENSE_BOTHEDGES, /*!< IOPORT sense both rising and falling edges */ + IOPORT_SENSE_RISING, /*!< IOPORT sense rising edges */ + IOPORT_SENSE_FALLING, /*!< IOPORT sense falling edges */ + }; +#endif + + +#if XMEGA +# include "xmega/ioport.h" +# if defined(IOPORT_XMEGA_COMPAT) +# include "xmega/ioport_compat.h" +# endif +#elif MEGA +# include "mega/ioport.h" +#elif UC3 +# include "uc3/ioport.h" +#elif SAM +# if SAM4L +# include "sam/ioport_gpio.h" +# else +# include "sam/ioport_pio.h" +# endif +#endif + +/** + * \brief Initializes the IOPORT service, ready for use. + * + * This function must be called before using any other functions in the IOPORT + * service. + */ + static inline void ioport_init (void) + { + arch_ioport_init (); + } + +/** + * \brief Enable an IOPORT pin, based on a pin created with \ref + * IOPORT_CREATE_PIN(). + * + * \param pin IOPORT pin to enable + */ + static inline void ioport_enable_pin (ioport_pin_t pin) + { + arch_ioport_enable_pin (pin); + } + +/** + * \brief Enable multiple pins in a single IOPORT port. + * + * \param port IOPORT port to enable + * \param mask Mask of pins within the port to enable + */ + static inline void ioport_enable_port (ioport_port_t port, + ioport_port_mask_t mask) + { + arch_ioport_enable_port (port, mask); + } + +/** + * \brief Disable IOPORT pin, based on a pin created with \ref + * IOPORT_CREATE_PIN(). + * + * \param pin IOPORT pin to disable + */ + static inline void ioport_disable_pin (ioport_pin_t pin) + { + arch_ioport_disable_pin (pin); + } + +/** + * \brief Disable multiple pins in a single IOPORT port. + * + * \param port IOPORT port to disable + * \param mask Pin mask of pins to disable + */ + static inline void ioport_disable_port (ioport_port_t port, + ioport_port_mask_t mask) + { + arch_ioport_disable_port (port, mask); + } + +/** + * \brief Set multiple pin modes in a single IOPORT port, such as pull-up, + * pull-down, etc. configuration. + * + * \param port IOPORT port to configure + * \param mask Pin mask of pins to configure + * \param mode Mode masks to configure for the specified pins (\ref + * ioport_modes) + */ + static inline void ioport_set_port_mode (ioport_port_t port, + ioport_port_mask_t mask, + ioport_mode_t mode) + { + arch_ioport_set_port_mode (port, mask, mode); + } + +/** + * \brief Set pin mode for one single IOPORT pin. + * + * \param pin IOPORT pin to configure + * \param mode Mode masks to configure for the specified pin (\ref ioport_modes) + */ + static inline void ioport_set_pin_mode (ioport_pin_t pin, + ioport_mode_t mode) + { + arch_ioport_set_pin_mode (pin, mode); + } + +/** + * \brief Reset multiple pin modes in a specified IOPORT port to defaults. + * + * \param port IOPORT port to configure + * \param mask Mask of pins whose mode configuration is to be reset + */ + static inline void ioport_reset_port_mode (ioport_port_t port, + ioport_port_mask_t mask) + { + arch_ioport_set_port_mode (port, mask, 0); + } + +/** + * \brief Reset pin mode configuration for a single IOPORT pin + * + * \param pin IOPORT pin to configure + */ + static inline void ioport_reset_pin_mode (ioport_pin_t pin) + { + arch_ioport_set_pin_mode (pin, 0); + } + +/** + * \brief Set I/O direction for a group of pins in a single IOPORT. + * + * \param port IOPORT port to configure + * \param mask Pin mask of pins to configure + * \param dir Direction to set for the specified pins (\ref ioport_direction) + */ + static inline void ioport_set_port_dir (ioport_port_t port, + ioport_port_mask_t mask, + enum ioport_direction dir) + { + arch_ioport_set_port_dir (port, mask, dir); + } + +/** + * \brief Set direction for a single IOPORT pin. + * + * \param pin IOPORT pin to configure + * \param dir Direction to set for the specified pin (\ref ioport_direction) + */ + static inline void ioport_set_pin_dir (ioport_pin_t pin, + enum ioport_direction dir) + { + arch_ioport_set_pin_dir (pin, dir); + } + +/** + * \brief Set an IOPORT pin to a specified logical value. + * + * \param pin IOPORT pin to configure + * \param level Logical value of the pin + */ + static inline void ioport_set_pin_level (ioport_pin_t pin, bool level) + { + arch_ioport_set_pin_level (pin, level); + } + +/** + * \brief Set a group of IOPORT pins in a single port to a specified logical + * value. + * + * \param port IOPORT port to write to + * \param mask Pin mask of pins to modify + * \param level Level of the pins to be modified + */ + static inline void ioport_set_port_level (ioport_port_t port, + ioport_port_mask_t mask, + ioport_port_mask_t level) + { + arch_ioport_set_port_level (port, mask, level); + } + +/** + * \brief Get current value of an IOPORT pin, which has been configured as an + * input. + * + * \param pin IOPORT pin to read + * \return Current logical value of the specified pin + */ + static inline bool ioport_get_pin_level (ioport_pin_t pin) + { + return arch_ioport_get_pin_level (pin); + } + +/** + * \brief Get current value of several IOPORT pins in a single port, which have + * been configured as an inputs. + * + * \param port IOPORT port to read + * \param mask Pin mask of pins to read + * \return Logical levels of the specified pins from the read port, returned as + * a mask. + */ + static inline ioport_port_mask_t ioport_get_port_level (ioport_pin_t port, + ioport_port_mask_t + mask) + { + return arch_ioport_get_port_level (port, mask); + } + +/** + * \brief Toggle the value of an IOPORT pin, which has previously configured as + * an output. + * + * \param pin IOPORT pin to toggle + */ + static inline void ioport_toggle_pin_level (ioport_pin_t pin) + { + arch_ioport_toggle_pin_level (pin); + } + +/** + * \brief Toggle the values of several IOPORT pins located in a single port. + * + * \param port IOPORT port to modify + * \param mask Pin mask of pins to toggle + */ + static inline void ioport_toggle_port_level (ioport_port_t port, + ioport_port_mask_t mask) + { + arch_ioport_toggle_port_level (port, mask); + } + +/** + * \brief Set the pin sense mode of a single IOPORT pin. + * + * \param pin IOPORT pin to configure + * \param pin_sense Edge to sense for the pin (\ref ioport_sense) + */ + static inline void ioport_set_pin_sense_mode (ioport_pin_t pin, + enum ioport_sense pin_sense) + { + arch_ioport_set_pin_sense_mode (pin, pin_sense); + } + +/** + * \brief Set the pin sense mode of a multiple IOPORT pins on a single port. + * + * \param port IOPORT port to configure + * \param mask Bitmask if pins whose edge sense is to be configured + * \param pin_sense Edge to sense for the pins (\ref ioport_sense) + */ + static inline void ioport_set_port_sense_mode (ioport_port_t port, + ioport_port_mask_t mask, + enum ioport_sense pin_sense) + { + arch_ioport_set_port_sense_mode (port, mask, pin_sense); + } + +/** + * \brief Convert a pin ID into a its port ID. + * + * \param pin IOPORT pin ID to convert + * \retval Port ID for the given pin ID + */ + static inline ioport_port_t ioport_pin_to_port_id (ioport_pin_t pin) + { + return arch_ioport_pin_to_port_id (pin); + } + +/** + * \brief Convert a pin ID into a bitmask mask for the given pin on its port. + * + * \param pin IOPORT pin ID to convert + * \retval Bitmask with a bit set that corresponds to the given pin ID in its port + */ + static inline ioport_port_mask_t ioport_pin_to_mask (ioport_pin_t pin) + { + return arch_ioport_pin_to_mask (pin); + } + +/** @} */ + +/** + * \page ioport_quickstart Quick start guide for the common IOPORT service + * + * This is the quick start guide for the \ref ioport_group, with + * step-by-step instructions on how to configure and use the service in a + * selection of use cases. + * + * The use cases contain several code fragments. The code fragments in the + * steps for setup can be copied into a custom initialization function, while + * the steps for usage can be copied into, e.g., the main application function. + * + * \section ioport_quickstart_basic Basic use case + * In this use case we will configure one IO pin for button input and one for + * LED control. Then it will read the button state and output it on the LED. + * + * \section ioport_quickstart_basic_setup Setup steps + * + * \subsection ioport_quickstart_basic_setup_code Example code + * \code + * #define MY_LED IOPORT_CREATE_PIN(PORTA, 5) + * #define MY_BUTTON IOPORT_CREATE_PIN(PORTA, 6) + * + * ioport_init(); + * + * ioport_set_pin_dir(MY_LED, IOPORT_DIR_OUTPUT); + * ioport_set_pin_dir(MY_BUTTON, IOPORT_DIR_INPUT); + * ioport_set_pin_mode(MY_BUTTON, IOPORT_MODE_PULLUP); + * \endcode + * + * \subsection ioport_quickstart_basic_setup_flow Workflow + * -# It's useful to give the GPIOs symbolic names and this can be done with + * the \ref IOPORT_CREATE_PIN macro. We define one for a LED and one for a + * button. + * - \code + * #define MY_LED IOPORT_CREATE_PIN(PORTA, 5) + * #define MY_BUTTON IOPORT_CREATE_PIN(PORTA, 6) + * \endcode + * - \note The usefulness of the \ref IOPORT_CREATE_PIN macro and port names + * differ between architectures: + * - MEGA, MEGA_RF and XMEGA: Use \ref IOPORT_CREATE_PIN macro with port definitions + * PORTA, PORTB ... + * - UC3: Most convenient to pick up the device header file pin definition + * and us it directly. E.g.: AVR32_PIN_PB06 + * - SAM: Most convenient to pick up the device header file pin definition + * and us it directly. E.g.: PIO_PA5_IDX
+ * \ref IOPORT_CREATE_PIN can also be used with port definitions + * PIOA, PIOB ... + * -# Initialize the ioport service. This typically enables the IO module if + * needed. + * - \code ioport_init(); \endcode + * -# Set the LED GPIO as output: + * - \code ioport_set_pin_dir(MY_LED, IOPORT_DIR_OUTPUT); \endcode + * -# Set the button GPIO as input: + * - \code ioport_set_pin_dir(MY_BUTTON, IOPORT_DIR_INPUT); \endcode + * -# Enable pull-up for the button GPIO: + * - \code ioport_set_pin_mode(MY_BUTTON, IOPORT_MODE_PULLUP); \endcode + * + * \section ioport_quickstart_basic_usage Usage steps + * + * \subsection ioport_quickstart_basic_usage_code Example code + * \code + * bool value; + * + * value = ioport_get_pin_level(MY_BUTTON); + * ioport_set_pin_level(MY_LED, value); + * \endcode + * + * \subsection ioport_quickstart_basic_usage_flow Workflow + * -# Define a boolean variable for state storage: + * - \code bool value; \endcode + * -# Read out the button level into variable value: + * - \code value = ioport_get_pin_level(MY_BUTTON); \endcode + * -# Set the LED to read out value from the button: + * - \code ioport_set_pin_level(MY_LED, value); \endcode + * + * \section ioport_quickstart_advanced Advanced use cases + * - \subpage ioport_quickstart_use_case_1 : Port access + */ + +/** + * \page ioport_quickstart_use_case_1 Advanced use case doing port access + * + * In this case we will read out the pins from one whole port and write the + * read value to another port. + * + * \section ioport_quickstart_use_case_1_setup Setup steps + * + * \subsection ioport_quickstart_use_case_1_setup_code Example code + * \code + * #define IN_PORT IOPORT_PORTA + * #define OUT_PORT IOPORT_PORTB + * #define MASK 0x00000060 + * + * ioport_init(); + * + * ioport_set_port_dir(IN_PORT, MASK, IOPORT_DIR_INPUT); + * ioport_set_port_dir(OUT_PORT, MASK, IOPORT_DIR_OUTPUT); + * \endcode + * + * \subsection ioport_quickstart_basic_setup_flow Workflow + * -# It's useful to give the ports symbolic names: + * - \code + * #define IN_PORT IOPORT_PORTA + * #define OUT_PORT IOPORT_PORTB + * \endcode + * - \note The port names differ between architectures: + * - MEGA_RF, MEGA and XMEGA: There are predefined names for ports: IOPORT_PORTA, + * IOPORT_PORTB ... + * - UC3: Use the index value of the different IO blocks: 0, 1 ... + * - SAM: There are predefined names for ports: IOPORT_PIOA, IOPORT_PIOB + * ... + * -# Also useful to define a mask for the bits to work with: + * - \code #define MASK 0x00000060 \endcode + * -# Initialize the ioport service. This typically enables the IO module if + * needed. + * - \code ioport_init(); \endcode + * -# Set one of the ports as input: + * - \code ioport_set_pin_dir(IN_PORT, MASK, IOPORT_DIR_INPUT); \endcode + * -# Set the other port as output: + * - \code ioport_set_pin_dir(OUT_PORT, MASK, IOPORT_DIR_OUTPUT); \endcode + * + * \section ioport_quickstart_basic_usage Usage steps + * + * \subsection ioport_quickstart_basic_usage_code Example code + * \code + * ioport_port_mask_t value; + * + * value = ioport_get_port_level(IN_PORT, MASK); + * ioport_set_port_level(OUT_PORT, MASK, value); + * \endcode + * + * \subsection ioport_quickstart_basic_usage_flow Workflow + * -# Define a variable for port date storage: + * - \code ioport_port_mask_t value; \endcode + * -# Read out from one port: + * - \code value = ioport_get_port_level(IN_PORT, MASK); \endcode + * -# Put the read data out on the other port: + * - \code ioport_set_port_level(OUT_PORT, MASK, value); \endcode + */ + +#ifdef __cplusplus +} +#endif + +#endif /* IOPORT_H */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/ioport/xmega/ioport.h b/bacnet-stack/ports/xplained/ASF/common/services/ioport/xmega/ioport.h new file mode 100644 index 00000000..e9f8a45b --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/ioport/xmega/ioport.h @@ -0,0 +1,367 @@ +/** + * \file + * + * \brief XMEGA architecture specific IOPORT service implementation header file. + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef IOPORT_XMEGA_H +#define IOPORT_XMEGA_H + +#define IOPORT_CREATE_PIN(port, pin) ((IOPORT_ ## port) * 8 + (pin)) +#define IOPORT_BASE_ADDRESS 0x600 +#define IOPORT_PORT_OFFSET 0x20 + +/** \name IOPORT port numbers */ +/** @{ */ +#if !XMEGA_B3 +# define IOPORT_PORTA 0 +#endif + +#define IOPORT_PORTB 1 +#define IOPORT_PORTC 2 +#define IOPORT_PORTD 3 + +#if !XMEGA_B3 +# define IOPORT_PORTE 4 +#endif + +#if XMEGA_A1 || XMEGA_A1U || XMEGA_A3 || XMEGA_A3U || XMEGA_A3B || XMEGA_A3BU ||\ + XMEGA_C3 || XMEGA_D3 +# define IOPORT_PORTF 5 +#endif + +#if XMEGA_B1 || XMEGA_B3 +# define IOPORT_PORTG 6 +#endif + +#if XMEGA_A1 || XMEGA_A1U +# define IOPORT_PORTH 7 +# define IOPORT_PORTJ 8 +# define IOPORT_PORTK 9 +#endif + +#if XMEGA_B1 || XMEGA_B3 +# define IOPORT_PORTM 11 +#endif + +#if XMEGA_A1 || XMEGA_A1U +# define IOPORT_PORTQ 14 +#endif + +#define IOPORT_PORTR 15 +/** @} */ + +/** + * \weakgroup ioport_group + * \section ioport_modes IOPORT Modes + * + * For details on these please see the XMEGA Manual. + * + * @{ + */ + +/** \name IOPORT Mode bit definitions */ +/** @{ */ +#define IOPORT_MODE_TOTEM (0x00 << 3) /*!< Totem-pole */ +#define IOPORT_MODE_BUSKEEPER (0x01 << 3) /*!< Buskeeper */ +#define IOPORT_MODE_PULLDOWN (0x02 << 3) /*!< Pull-down */ +#define IOPORT_MODE_PULLUP (0x03 << 3) /*!< Pull-up */ +#define IOPORT_MODE_WIREDOR (0x04 << 3) /*!< Wired OR */ +#define IOPORT_MODE_WIREDAND (0x05 << 3) /*!< Wired AND */ +#define IOPORT_MODE_WIREDORPULL (0x06 << 3) /*!< Wired OR with pull-down */ +#define IOPORT_MODE_WIREDANDPULL (0x07 << 3) /*!< Wired AND with pull-up */ +#define IOPORT_MODE_INVERT_PIN (0x01 << 6) /*!< Invert output and input */ +#define IOPORT_MODE_SLEW_RATE_LIMIT (0x01 << 7) /*!< Slew rate limiting */ +/** @} */ + +/** @} */ + +typedef uint8_t ioport_mode_t; +typedef uint8_t ioport_pin_t; +typedef uint8_t ioport_port_t; +typedef uint8_t ioport_port_mask_t; + +__always_inline static ioport_port_t +arch_ioport_pin_to_port_id (ioport_pin_t pin) +{ + return pin >> 3; +} + +__always_inline static PORT_t * +arch_ioport_port_to_base (ioport_port_t port) +{ + return (PORT_t *) ((uintptr_t) IOPORT_BASE_ADDRESS + + (port * IOPORT_PORT_OFFSET)); +} + +__always_inline static PORT_t * +arch_ioport_pin_to_base (ioport_pin_t pin) +{ + return arch_ioport_port_to_base (arch_ioport_pin_to_port_id (pin)); +} + +__always_inline static ioport_port_mask_t +arch_ioport_pin_to_mask (ioport_pin_t pin) +{ + return 1U << (pin & 0x07); +} + +__always_inline static ioport_port_mask_t +arch_ioport_pin_to_index (ioport_pin_t pin) +{ + return (pin & 0x07); +} + +__always_inline static void +arch_ioport_init (void) +{ + +} + +__always_inline static void +arch_ioport_enable_port (ioport_port_t port, ioport_port_mask_t mask) +{ + PORT_t *base = arch_ioport_port_to_base (port); + volatile uint8_t *pin_ctrl = &base->PIN0CTRL; + + uint8_t flags = cpu_irq_save (); + + for (uint8_t i = 0; i < 8; i++) + { + if (mask & arch_ioport_pin_to_mask (i)) + { + pin_ctrl[i] &= ~PORT_ISC_gm; + } + } + + cpu_irq_restore (flags); +} + +__always_inline static void +arch_ioport_enable_pin (ioport_pin_t pin) +{ + PORT_t *base = arch_ioport_pin_to_base (pin); + volatile uint8_t *pin_ctrl = + (&base->PIN0CTRL + arch_ioport_pin_to_index (pin)); + + uint8_t flags = cpu_irq_save (); + + *pin_ctrl &= ~PORT_ISC_gm; + + cpu_irq_restore (flags); +} + +__always_inline static void +arch_ioport_disable_port (ioport_port_t port, ioport_port_mask_t mask) +{ + PORT_t *base = arch_ioport_port_to_base (port); + volatile uint8_t *pin_ctrl = &base->PIN0CTRL; + + uint8_t flags = cpu_irq_save (); + + for (uint8_t i = 0; i < 8; i++) + { + if (mask & arch_ioport_pin_to_mask (i)) + { + pin_ctrl[i] |= PORT_ISC_INPUT_DISABLE_gc; + } + } + + cpu_irq_restore (flags); +} + +__always_inline static void +arch_ioport_disable_pin (ioport_pin_t pin) +{ + PORT_t *base = arch_ioport_pin_to_base (pin); + volatile uint8_t *pin_ctrl = + (&base->PIN0CTRL + arch_ioport_pin_to_index (pin)); + + uint8_t flags = cpu_irq_save (); + + *pin_ctrl |= PORT_ISC_INPUT_DISABLE_gc; + + cpu_irq_restore (flags); +} + +__always_inline static void +arch_ioport_set_port_mode (ioport_port_t port, + ioport_port_mask_t mask, ioport_mode_t mode) +{ + PORT_t *base = arch_ioport_port_to_base (port); + + PORTCFG.MPCMASK = mask; + base->PIN0CTRL = mode; +} + +__always_inline static void +arch_ioport_set_pin_mode (ioport_pin_t pin, ioport_mode_t mode) +{ + PORT_t *base = arch_ioport_pin_to_base (pin); + + PORTCFG.MPCMASK = arch_ioport_pin_to_mask (pin); + base->PIN0CTRL = mode; +} + +__always_inline static void +arch_ioport_set_port_dir (ioport_port_t port, + ioport_port_mask_t mask, enum ioport_direction dir) +{ + PORT_t *base = arch_ioport_port_to_base (port); + + if (dir == IOPORT_DIR_OUTPUT) + { + base->DIRSET = mask; + } + else if (dir == IOPORT_DIR_INPUT) + { + base->DIRCLR = mask; + } +} + +__always_inline static void +arch_ioport_set_pin_dir (ioport_pin_t pin, enum ioport_direction dir) +{ + PORT_t *base = arch_ioport_pin_to_base (pin); + + if (dir == IOPORT_DIR_OUTPUT) + { + base->DIRSET = arch_ioport_pin_to_mask (pin); + } + else if (dir == IOPORT_DIR_INPUT) + { + base->DIRCLR = arch_ioport_pin_to_mask (pin); + } +} + +__always_inline static void +arch_ioport_set_pin_level (ioport_pin_t pin, bool level) +{ + PORT_t *base = arch_ioport_pin_to_base (pin); + + if (level) + { + base->OUTSET = arch_ioport_pin_to_mask (pin); + } + else + { + base->OUTCLR = arch_ioport_pin_to_mask (pin); + } +} + +__always_inline static void +arch_ioport_set_port_level (ioport_port_t port, + ioport_port_mask_t mask, ioport_port_mask_t level) +{ + PORT_t *base = arch_ioport_port_to_base (port); + + base->OUTSET = mask & level; + base->OUTCLR = mask & ~level; +} + +__always_inline static bool +arch_ioport_get_pin_level (ioport_pin_t pin) +{ + PORT_t *base = arch_ioport_pin_to_base (pin); + + return base->IN & arch_ioport_pin_to_mask (pin); +} + +__always_inline static ioport_port_mask_t +arch_ioport_get_port_level (ioport_port_t port, ioport_port_mask_t mask) +{ + PORT_t *base = arch_ioport_port_to_base (port); + + return base->IN & mask; +} + +__always_inline static void +arch_ioport_toggle_pin_level (ioport_pin_t pin) +{ + PORT_t *base = arch_ioport_pin_to_base (pin); + + base->OUTTGL = arch_ioport_pin_to_mask (pin); +} + +__always_inline static void +arch_ioport_toggle_port_level (ioport_port_t port, ioport_port_mask_t mask) +{ + PORT_t *base = arch_ioport_port_to_base (port); + + base->OUTTGL = mask; +} + +__always_inline static void +arch_ioport_set_pin_sense_mode (ioport_pin_t pin, enum ioport_sense pin_sense) +{ + PORT_t *base = arch_ioport_pin_to_base (pin); + volatile uint8_t *pin_ctrl = + (&base->PIN0CTRL + arch_ioport_pin_to_index (pin)); + + uint8_t flags = cpu_irq_save (); + + *pin_ctrl &= ~PORT_ISC_gm; + *pin_ctrl |= (pin_sense & PORT_ISC_gm); + + cpu_irq_restore (flags); +} + +__always_inline static void +arch_ioport_set_port_sense_mode (ioport_port_t port, + ioport_port_mask_t mask, + enum ioport_sense pin_sense) +{ + PORT_t *base = arch_ioport_port_to_base (port); + volatile uint8_t *pin_ctrl = &base->PIN0CTRL; + uint8_t new_sense_bits = (pin_sense & PORT_ISC_gm); + + uint8_t flags = cpu_irq_save (); + + for (uint8_t i = 0; i < 8; i++) + { + if (mask & arch_ioport_pin_to_mask (i)) + { + pin_ctrl[i] = (pin_ctrl[i] & ~PORT_ISC_gm) | new_sense_bits; + } + } + + cpu_irq_restore (flags); +} + +#endif /* IOPORT_XMEGA_H */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/ioport/xmega/ioport_compat.c b/bacnet-stack/ports/xplained/ASF/common/services/ioport/xmega/ioport_compat.c new file mode 100644 index 00000000..3db18206 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/ioport/xmega/ioport_compat.c @@ -0,0 +1,71 @@ +/** + * \file + * + * \brief XMEGA legacy IOPORT software compatibility driver interface. + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include "ioport_compat.h" + +#if defined(IOPORT_XMEGA_COMPAT) +void ioport_configure_port_pin(void *port, + pin_mask_t pin_mask, + port_pin_flags_t flags) +{ + uint8_t pin; + + for (pin = 0; pin < 8; pin++) { + if (pin_mask & (1 << pin)) { + *((uint8_t *) port + PORT_PIN0CTRL + pin) = flags >> 8; + } + } + /* Select direction and initial pin state */ + if (flags & IOPORT_DIR_OUTPUT) { + if (flags & IOPORT_INIT_HIGH) { + *((uint8_t *) port + PORT_OUTSET) = pin_mask; + } else { + *((uint8_t *) port + PORT_OUTCLR) = pin_mask; + } + + *((uint8_t *) port + PORT_DIRSET) = pin_mask; + } else { + *((uint8_t *) port + PORT_DIRCLR) = pin_mask; + } +} + +#endif diff --git a/bacnet-stack/ports/xplained/ASF/common/services/ioport/xmega/ioport_compat.h b/bacnet-stack/ports/xplained/ASF/common/services/ioport/xmega/ioport_compat.h new file mode 100644 index 00000000..df0dd67b --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/ioport/xmega/ioport_compat.h @@ -0,0 +1,332 @@ +/** + * \file + * + * \brief XMEGA legacy IOPORT software compatibility driver interface header + * file. + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef IOPORT_XMEGA_COMPAT_H_ +#define IOPORT_XMEGA_COMPAT_H_ + +#include "../ioport.h" + +/** + * \brief A pin mask + * + * This type is used to describe the port pin mask on the part. + */ +typedef uint8_t pin_mask_t; + +/** + * \brief A PORT pin + * + * This type is used to describe the PORT pins on the part. + */ +typedef uint8_t port_pin_t; + +/** + * \brief Pin configuration flags + * + * This is a bitmask containing configuration flags for the pins that shall be + * configured. + */ +typedef uint16_t port_pin_flags_t; + +/** + * \brief A port id + * + * This type is used to describe the port id on the part (0 is PORTA). + */ +typedef uint8_t port_id_t; + +/** \name Initial Output State Flags */ +/** @{ */ +#define IOPORT_INIT_LOW (0 << 1) /*!< Initial Output State Low */ +#define IOPORT_INIT_HIGH (1 << 1) /*!< Initial Output State High */ +/** @} */ + +/** \name Input/Sense Configuration Flags */ +/** @{ */ +#define IOPORT_BOTHEDGES (0 << 8) /*!< Sense Both Edges */ +#define IOPORT_RISING (1 << 8) /*!< Sense Rising Edge */ +#define IOPORT_FALLING (2 << 8) /*!< Sense Falling Edge */ +#define IOPORT_LEVEL (3 << 8) /*!< Sense Low Level */ +#if XMEGA_E +# define IOPORT_FORCE_ENABLE (6 << 8) /*!< Sense Force Input Enable Low Level */ +#endif +#define IOPORT_INPUT_DISABLE (7 << 8) /*!< Input Buffer Disabled */ +/** @} */ + +/** \name Output and Pull Configuration Flags */ +/** @{ */ +#define IOPORT_TOTEM (0 << 11) /*!< Normal push/pull output */ +#define IOPORT_BUSKEEPER (1 << 11) /*!< Bus Keeper */ +#define IOPORT_PULL_DOWN (2 << 11) /*!< Pull-Down (when input) */ +#define IOPORT_PULL_UP (3 << 11) /*!< Pull-Up (when input) */ +#define IOPORT_WIRED_OR (4 << 11) /*!< Wired OR */ +#define IOPORT_WIRED_AND (5 << 11) /*!< Wired AND */ +#define IOPORT_WIRED_OR_PULL_DOWN (6 << 11) /*!< Wired OR and Pull-Down */ +#define IOPORT_WIRED_AND_PULL_UP (7 << 11) /*!< Wired AND and Pull-Up */ +/** @} */ + +/** \name Inverted I/O Configuration Flags */ +/** @{ */ +#define IOPORT_INV_ENABLED (1 << 14) /*!< I/O is Inverted */ +#define IOPORT_INV_DISABLE (0 << 14) /*!< I/O is Not Inverted */ +/** @} */ + +/** \name Slew Rate Limit Configuration Flags */ +/** @{ */ +#define IOPORT_SRL_ENABLED (1 << 15) /*!< Slew Rate Limit Enabled */ +#define IOPORT_SRL_DISABLED (0 << 15) /*!< Slew Rate Limit Disabled */ +/** @} */ + +/** + * \internal + * \name PORT fields structure offset + * + * These macros are used to compute the field offset number with the PORT_t + * structure. + */ +/** @{ */ +#define PORT_DIR 0x00 /*!< Data Direction */ +#define PORT_DIRSET 0x01 /*!< Data Direction Set */ +#define PORT_DIRCLR 0x02 /*!< Data Direction Clear */ +#define PORT_DIRTGL 0x03 /*!< Data Direction Toggle */ +#define PORT_OUT 0x04 /*!< Data Output Value */ +#define PORT_OUTSET 0x05 /*!< Data Output Value Set */ +#define PORT_OUTCLR 0x06 /*!< Data Output Value Clear */ +#define PORT_OUTTGL 0x07 /*!< Data Output Value Toggle */ +#define PORT_IN 0x08 /*!< Data Input Value */ +#define PORT_INTCTRL 0x09 /*!< Interrupt Control */ +#define PORT_INT0MASK 0x0A /*!< Interrupt 0 Mask */ +#define PORT_INT1MASK 0x0B /*!< Interrupt 1 Mask */ +#define PORT_INTFLAGS 0x0C /*!< Interrupt Flags */ +#define PORT_PIN0CTRL 0x10 /*!< Pin 0 Configuration */ +#define PORT_PIN1CTRL 0x11 /*!< Pin 1 Configuration */ +#define PORT_PIN2CTRL 0x12 /*!< Pin 2 Configuration */ +#define PORT_PIN3CTRL 0x13 /*!< Pin 3 Configuration */ +#define PORT_PIN4CTRL 0x14 /*!< Pin 4 Configuration */ +#define PORT_PIN5CTRL 0x15 /*!< Pin 5 Configuration */ +#define PORT_PIN6CTRL 0x16 /*!< Pin 6 Configuration */ +#define PORT_PIN7CTRL 0x17 /*!< Pin 7 Configuration */ +/** @} */ + +static inline PORT_t * +ioport_pin_to_port (port_pin_t pin) +{ + return arch_ioport_pin_to_base (pin); +} + +static inline PORT_t * +ioport_id_pin_to_port (port_id_t port) +{ + return arch_ioport_port_to_base (port); +} + +/** + * \brief Configure the IO PORT pin function for a set of pins on a port + * + * \param port Pointer to the port + * \param pin_mask Mask containing the pins that should be configured + * \param flags Bitmask of flags specifying additional configuration + * parameters. + */ +void ioport_configure_port_pin (void *port, pin_mask_t pin_mask, + port_pin_flags_t flags); + +/** + * \brief Select the port function for a single pin + * + * \param pin The pin to configure + * \param flags Bitmask of flags specifying additional configuration + * parameters. + */ +static inline void +ioport_configure_pin (port_pin_t pin, port_pin_flags_t flags) +{ + ioport_configure_port_pin (arch_ioport_pin_to_base (pin), + arch_ioport_pin_to_mask (pin), flags); +} + +/** + * \brief Configure a group of I/O pins on a specified port number + * + * \param port The port number + * \param pin_mask The pin mask to configure + * \param flags Bitmask of flags specifying additional configuration + * parameters. + */ +static inline void +ioport_configure_group (port_id_t port, pin_mask_t pin_mask, + port_pin_flags_t flags) +{ + ioport_configure_port_pin (arch_ioport_port_to_base (port), pin_mask, + flags); +} + +/** + * \brief Drive a PORT pin to a given state + * + * This function will only have an effect if \a pin is configured as + * an output. + * + * \param pin A number identifying the pin to act on. + * \param value The desired state of the pin. \a true means drive the + * pin high (towards Vdd), while \a false means drive the pin low + * (towards Vss). + */ +static inline void +ioport_set_value (port_pin_t pin, bool value) +{ + arch_ioport_set_pin_level (pin, value); +} + +/** + * \brief Drive a PORT pin to a low level + * + * This function will only have an effect if \a pin is configured as + * an output. + * + * \param pin A number identifying the pin to act on. + */ +static inline void +ioport_set_pin_low (port_pin_t pin) +{ + arch_ioport_set_pin_level (pin, false); +} + +/** + * \brief Drive a PORT pin to a high level + * + * This function will only have an effect if \a pin is configured as + * an output. + * + * \param pin A number identifying the pin to act on. + */ +static inline void +ioport_set_pin_high (port_pin_t pin) +{ + arch_ioport_set_pin_level (pin, true); +} + +/** + * \brief Read the current state of a PORT pin + * + * \param pin A number identifying the pin to read. + * \retval true The pin is currently high (close to Vdd) + * \retval false The pin is currently low (close to Vss) + */ +static inline bool +ioport_get_value (port_pin_t pin) +{ + return arch_ioport_get_pin_level (pin); +} + +/** + * \brief Read the current state of a PORT pin and test high level + * + * \param pin A number identifying the pin to read. + * \retval true The pin is currently high (close to Vdd) + * \retval false The pin is currently low (close to Vss) + */ +static inline bool +ioport_pin_is_high (port_pin_t pin) +{ + return (arch_ioport_get_pin_level (pin) == true); +} + +/** + * \brief Read the current state of a PORT pin and test high level + * + * \param pin A number identifying the pin to read. + * \retval true The pin is currently high (close to Vdd) + * \retval false The pin is currently low (close to Vss) + */ +static inline bool +ioport_pin_is_low (port_pin_t pin) +{ + return (arch_ioport_get_pin_level (pin) == false); +} + +/** + * \brief Toggle the current state of a PORT pin + * + * \param pin A number identifying the pin to act on. + */ +static inline void +ioport_toggle_pin (port_pin_t pin) +{ + arch_ioport_toggle_pin_level (pin); +} + +/*! \brief Drives a group of I/O pin of a port to high level. + * + * \param port_id The port number. + * \param port_mask The mask. + */ +static inline void +ioport_set_group_high (port_id_t port_id, pin_mask_t port_mask) +{ + arch_ioport_set_port_level (port_id, port_mask, port_mask); +} + +/*! \brief Drives a group of I/O pin of a port to low level. + * + * \param port_id The port number. + * \param port_mask The mask. + */ +static inline void +ioport_set_group_low (port_id_t port_id, pin_mask_t port_mask) +{ + arch_ioport_set_port_level (port_id, port_mask, 0); +} + +/*! \brief Toggles a group of I/O pin of a port. + * + * \param port_id The port number. + * \param port_mask The mask. + */ +static inline void +ioport_tgl_group (port_id_t port_id, pin_mask_t port_mask) +{ + arch_ioport_toggle_port_level (port_id, port_mask); +} + +#endif /* IOPORT_COMPAT_H_ */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/serial/serial.h b/bacnet-stack/ports/xplained/ASF/common/services/serial/serial.h new file mode 100644 index 00000000..18fd3d19 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/serial/serial.h @@ -0,0 +1,264 @@ +/** + * \file + * + * \brief Serial Mode management + * + * Copyright (c) 2010 - 2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef SERIAL_H_INCLUDED +#define SERIAL_H_INCLUDED + +#include +#include "status_codes.h" + +/** + * \typedef usart_if + * + * This type can be used independently to refer to USART module for the + * architecture used. It refers to the correct type definition for the + * architecture, ie. USART_t* for XMEGA or avr32_usart_t* for UC3. + */ + +#if XMEGA +# include "xmega_usart/usart_serial.h" +#elif MEGA_RF +# include "megarf_usart/usart_serial.h" +#elif UC3 +# include "uc3_usart/usart_serial.h" +#elif SAM +# include "sam_uart/uart_serial.h" +#else +# error Unsupported chip type +#endif + +/** + * + * \defgroup serial_group Serial Interface (Serial) + * + * See \ref serial_quickstart. + * + * This is the common API for serial interface. Additional features are available + * in the documentation of the specific modules. + * + * \section serial_group_platform Platform Dependencies + * + * The serial API is partially chip- or platform-specific. While all + * platforms provide mostly the same functionality, there are some + * variations around how different bus types and clock tree structures + * are handled. + * + * The following functions are available on all platforms, but there may + * be variations in the function signature (i.e. parameters) and + * behaviour. These functions are typically called by platform-specific + * parts of drivers, and applications that aren't intended to be + * portable: + * - usart_serial_init() + * - usart_serial_putchar() + * - usart_serial_getchar() + * - usart_serial_write_packet() + * - usart_serial_read_packet() + * + * + * @{ + */ + +//! @} + +/** + * \page serial_quickstart Quick start guide for Serial Interface service + * + * This is the quick start guide for the \ref serial_group "Serial Interface module", with + * step-by-step instructions on how to configure and use the serial in a + * selection of use cases. + * + * The use cases contain several code fragments. The code fragments in the + * steps for setup can be copied into a custom initialization function, while + * the steps for usage can be copied into, e.g., the main application function. + * + * \section serial_use_cases Serial use cases + * - \ref serial_basic_use_case + * - \subpage serial_use_case_1 + * + * \section serial_basic_use_case Basic use case - transmit a character + * In this use case, the serial module is configured for: + * - Using USARTD0 + * - Baudrate: 9600 + * - Character length: 8 bit + * - Parity mode: Disabled + * - Stop bit: None + * - RS232 mode + * + * The use case waits for a received character on the configured USART and + * echoes the character back to the same USART. + * + * \section serial_basic_use_case_setup Setup steps + * + * \subsection serial_basic_use_case_setup_prereq Prerequisites + * -# \ref sysclk_group "System Clock Management (sysclk)" + * + * \subsection serial_basic_use_case_setup_code Example code + * The following configuration must be added to the project (typically to a + * conf_serial.h file, but it can also be added to your main application file.) + * \code + * #define USART_SERIAL &USARTD0 + * #define USART_SERIAL_BAUDRATE 9600 + * #define USART_SERIAL_CHAR_LENGTH USART_CHSIZE_8BIT_gc + * #define USART_SERIAL_PARITY USART_PMODE_DISABLED_gc + * #define USART_SERIAL_STOP_BIT false + * \endcode + * + * A variable for the received byte must be added: + * \code uint8_t received_byte; \endcode + * + * Add to application initialization: + * \code + * sysclk_init(); + * + * static usart_serial_options_t usart_options = { + * .baudrate = USART_SERIAL_BAUDRATE, + * .charlength = USART_SERIAL_CHAR_LENGTH, + * .paritytype = USART_SERIAL_PARITY, + * .stopbits = USART_SERIAL_STOP_BIT + * }; + * + * usart_serial_init(USART_SERIAL, &usart_options); + * \endcode + * + * \subsection serial_basic_use_case_setup_flow Workflow + * -# Initialize system clock: + * - \code sysclk_init(); \endcode + * -# Create serial USART options struct: + * - \code + * static usart_serial_options_t usart_options = { + * .baudrate = USART_SERIAL_BAUDRATE, + * .charlength = USART_SERIAL_CHAR_LENGTH, + * .paritytype = USART_SERIAL_PARITY, + * .stopbits = USART_SERIAL_STOP_BIT + * }; + * \endcode + * -# Initialize the serial service: + * - \code usart_serial_init(USART_SERIAL, &usart_options);\endcode + * + * \section serial_basic_use_case_usage Usage steps + * + * \subsection serial_basic_use_case_usage_code Example code + * Add to application C-file: + * \code + * usart_serial_getchar(USART_SERIAL, &received_byte); + * usart_serial_putchar(USART_SERIAL, received_byte); + * \endcode + * + * \subsection serial_basic_use_case_usage_flow Workflow + * -# Wait for reception of a character: + * - \code usart_serial_getchar(USART_SERIAL, &received_byte); \endcode + * -# Echo the character back: + * - \code usart_serial_putchar(USART_SERIAL, received_byte); \endcode + */ + +/** + * \page serial_use_case_1 Advanced use case - Send a packet of serial data + * + * In this use case, the USART module is configured for: + * - Using USARTD0 + * - Baudrate: 9600 + * - Character length: 8 bit + * - Parity mode: Disabled + * - Stop bit: None + * - RS232 mode + * + * The use case sends a string of text through the USART. + * + * \section serial_use_case_1_setup Setup steps + * + * \subsection serial_use_case_1_setup_prereq Prerequisites + * -# \ref sysclk_group "System Clock Management (sysclk)" + * + * \subsection serial_use_case_1_setup_code Example code + * The following configuration must be added to the project (typically to a + * conf_serial.h file, but it can also be added to your main application file.): + * \code + * #define USART_SERIAL &USARTD0 + * #define USART_SERIAL_BAUDRATE 9600 + * #define USART_SERIAL_CHAR_LENGTH USART_CHSIZE_8BIT_gc + * #define USART_SERIAL_PARITY USART_PMODE_DISABLED_gc + * #define USART_SERIAL_STOP_BIT false + * \endcode + * + * Add to application initialization: + * \code + * sysclk_init(); + * + * static usart_serial_options_t usart_options = { + * .baudrate = USART_SERIAL_BAUDRATE, + * .charlength = USART_SERIAL_CHAR_LENGTH, + * .paritytype = USART_SERIAL_PARITY, + * .stopbits = USART_SERIAL_STOP_BIT + * }; + * + * usart_serial_init(USART_SERIAL, &usart_options); + * \endcode + * + * \subsection serial_use_case_1_setup_flow Workflow + * -# Initialize system clock: + * - \code sysclk_init(); \endcode + * -# Create USART options struct: + * - \code + * static usart_serial_options_t usart_options = { + * .baudrate = USART_SERIAL_BAUDRATE, + * .charlength = USART_SERIAL_CHAR_LENGTH, + * .paritytype = USART_SERIAL_PARITY, + * .stopbits = USART_SERIAL_STOP_BIT + * }; + * \endcode + * -# Initialize in RS232 mode: + * - \code usart_serial_init(USART_SERIAL_EXAMPLE, &usart_options); \endcode + * + * \section serial_use_case_1_usage Usage steps + * + * \subsection serial_use_case_1_usage_code Example code + * Add to, e.g., main loop in application C-file: + * \code + * usart_serial_write_packet(USART_SERIAL, "Test String", strlen("Test String")); + * \endcode + * + * \subsection serial_use_case_1_usage_flow Workflow + * -# Write a string of text to the USART: + * - \code usart_serial_write_packet(USART_SERIAL, "Test String", strlen("Test String")); \endcode + */ + +#endif /* SERIAL_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/serial/usart_serial.c b/bacnet-stack/ports/xplained/ASF/common/services/serial/usart_serial.c new file mode 100644 index 00000000..d526f691 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/serial/usart_serial.c @@ -0,0 +1,83 @@ +/** + * + * \file + * + * \brief USART Serial driver functions. + * + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include "serial.h" + +/** + * \brief Send a sequence of bytes to USART device + * + * \param usart Base address of the USART instance. + * \param data Data buffer to read + * \param len Length of data + * + */ +status_code_t usart_serial_write_packet(usart_if usart, const uint8_t *data, + size_t len) +{ + while (len) { + usart_serial_putchar(usart, *data); + len--; + data++; + } + return STATUS_OK; +} + +/** + * \brief Receive a sequence of bytes from USART device + * + * \param usart Base address of the USART instance. + * \param data Data buffer to write + * \param len Length of data + * + */ +status_code_t usart_serial_read_packet(usart_if usart, uint8_t *data, + size_t len) +{ + while (len) { + usart_serial_getchar(usart, data); + len--; + data++; + } + return STATUS_OK; +} diff --git a/bacnet-stack/ports/xplained/ASF/common/services/serial/xmega_usart/usart_serial.h b/bacnet-stack/ports/xplained/ASF/common/services/serial/xmega_usart/usart_serial.h new file mode 100644 index 00000000..9b6e7844 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/serial/xmega_usart/usart_serial.h @@ -0,0 +1,170 @@ +/** + * \file + * + * This file defines a useful set of functions for the Serial interface on AVR + * XMEGA devices. + * + * Copyright (c) 2009-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _USART_SERIAL_H_ +#define _USART_SERIAL_H_ + +#include "compiler.h" +#include "sysclk.h" +#include "status_codes.h" +#include "usart.h" + +/*! \name Serial Management Configuration + */ +//! @{ +#include "conf_usart_serial.h" +//! @} + +typedef usart_rs232_options_t usart_serial_options_t; + +typedef USART_t *usart_if; + +/*! \brief Initializes the Usart in master mode. + * + * \param usart Base address of the USART instance. + * \param options Options needed to set up RS232 communication (see \ref usart_serial_options_t). + * + * \retval true if the initialization was successful + * \retval false if initialization failed (error in baud rate calculation) + */ +static inline bool usart_serial_init(usart_if usart, const + usart_serial_options_t *options) +{ + // USART options. + usart_rs232_options_t usart_rs232_options; + usart_rs232_options.charlength = options->charlength; + usart_rs232_options.paritytype = options->paritytype; + usart_rs232_options.stopbits = options->stopbits; + usart_rs232_options.baudrate = options->baudrate; + +#ifdef USARTC0 + if((uint16_t)usart == (uint16_t)&USARTC0) { + sysclk_enable_module(SYSCLK_PORT_C,PR_USART0_bm); + } +#endif +#ifdef USARTC1 + if((uint16_t)usart == (uint16_t)&USARTC1) { + sysclk_enable_module(SYSCLK_PORT_C,PR_USART1_bm); + } +#endif +#ifdef USARTD0 + if((uint16_t)usart == (uint16_t)&USARTD0) { + sysclk_enable_module(SYSCLK_PORT_D,PR_USART0_bm); + } +#endif +#ifdef USARTD1 + if((uint16_t)usart == (uint16_t)&USARTD1) { + sysclk_enable_module(SYSCLK_PORT_D,PR_USART1_bm); + } +#endif +#ifdef USARTE0 + if((uint16_t)usart == (uint16_t)&USARTE0) { + sysclk_enable_module(SYSCLK_PORT_E,PR_USART0_bm); + } +#endif +#ifdef USARTE1 + if((uint16_t)usart == (uint16_t)&USARTE1) { + sysclk_enable_module(SYSCLK_PORT_E,PR_USART1_bm); + } +#endif +#ifdef USARTF0 + if((uint16_t)usart == (uint16_t)&USARTF0) { + sysclk_enable_module(SYSCLK_PORT_F,PR_USART0_bm); + } +#endif +#ifdef USARTF1 + if((uint16_t)usart == (uint16_t)&USARTF1) { + sysclk_enable_module(SYSCLK_PORT_F,PR_USART1_bm); + } +#endif + if (usart_init_rs232(usart, &usart_rs232_options)) { + return true; + } + else { + return false; + } +} + +/*! \brief Sends a character with the USART. + * + * \param usart Base address of the USART instance. + * \param c Character to write. + * + * \return Status code + */ +static inline enum status_code usart_serial_putchar(usart_if usart, uint8_t c) +{ + return usart_putchar(usart, c); +} +/*! \brief Waits until a character is received, and returns it. + * + * \param usart Base address of the USART instance. + * \param data Data to read + * + */ +static inline void usart_serial_getchar(usart_if usart, uint8_t *data) +{ + *data = usart_getchar(usart); +} + +/** + * \brief Send a sequence of bytes to USART device + * + * \param usart Base address of the USART instance. + * \param data Data buffer to read + * \param len Length of data + * + */ +extern status_code_t usart_serial_write_packet(usart_if usart, const uint8_t *data, size_t len); + +/** + * \brief Receive a sequence of bytes from USART device + * + * \param usart Base address of the USART instance. + * \param data Data buffer to write + * \param len Length of data + * + */ +extern status_code_t usart_serial_read_packet(usart_if usart, uint8_t *data, size_t len); + +#endif // _USART_SERIAL_H_ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/sleepmgr/sleepmgr.h b/bacnet-stack/ports/xplained/ASF/common/services/sleepmgr/sleepmgr.h new file mode 100644 index 00000000..ea98de44 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/sleepmgr/sleepmgr.h @@ -0,0 +1,252 @@ +/** + * \file + * + * \brief Sleep manager + * + * Copyright (c) 2010 - 2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef SLEEPMGR_H +#define SLEEPMGR_H + +#include +#include + +#if (SAM3S || SAM3U || SAM3N || SAM3XA || SAM4S || SAM4E) +# include "sam/sleepmgr.h" +#elif XMEGA +# include "xmega/sleepmgr.h" +#elif UC3 +# include "uc3/sleepmgr.h" +#elif SAM4L +# include "sam4l/sleepmgr.h" +#else +# error Unsupported device. +#endif + +/** + * \defgroup sleepmgr_group Sleep manager + * + * The sleep manager is a service for ensuring that the device is not put to + * sleep in deeper sleep modes than the system (e.g., peripheral drivers, + * services or the application) allows at any given time. + * + * It is based on the use of lock counting for the individual sleep modes, and + * will put the device to sleep in the shallowest sleep mode that has a non-zero + * lock count. The drivers/services/application can change these counts by use + * of \ref sleepmgr_lock_mode and \ref sleepmgr_unlock_mode. + * Refer to \ref sleepmgr_mode for a list of the sleep modes available for + * locking, and the device datasheet for information on their effect. + * + * The application must supply the file \ref conf_sleepmgr.h. + * + * For the sleep manager to be enabled, the symbol \ref CONFIG_SLEEPMGR_ENABLE + * must be defined, e.g., in \ref conf_sleepmgr.h. If this symbol is not + * defined, the functions are replaced with dummy functions and no RAM is used. + * + * @{ + */ + +/** + * \def CONFIG_SLEEPMGR_ENABLE + * \brief Configuration symbol for enabling the sleep manager + * + * If this symbol is not defined, the functions of this service are replaced + * with dummy functions. This is useful for reducing code size and execution + * time if the sleep manager is not needed in the application. + * + * This symbol may be defined in \ref conf_sleepmgr.h. + */ +#if defined(__DOXYGEN__) && !defined(CONFIG_SLEEPMGR_ENABLE) +# define CONFIG_SLEEPMGR_ENABLE +#endif + +/** + * \enum sleepmgr_mode + * \brief Sleep mode locks + * + * Identifiers for the different sleep mode locks. + */ + +/** + * \brief Initialize the lock counts + * + * Sets all lock counts to 0, except the very last one, which is set to 1. This + * is done to simplify the algorithm for finding the deepest allowable sleep + * mode in \ref sleepmgr_enter_sleep. + */ +static inline void +sleepmgr_init (void) +{ +#ifdef CONFIG_SLEEPMGR_ENABLE + uint8_t i; + + for (i = 0; i < SLEEPMGR_NR_OF_MODES - 1; i++) + { + sleepmgr_locks[i] = 0; + } + sleepmgr_locks[SLEEPMGR_NR_OF_MODES - 1] = 1; +#endif /* CONFIG_SLEEPMGR_ENABLE */ +} + +/** + * \brief Increase lock count for a sleep mode + * + * Increases the lock count for \a mode to ensure that the sleep manager does + * not put the device to sleep in the deeper sleep modes. + * + * \param mode Sleep mode to lock. + */ +static inline void +sleepmgr_lock_mode (enum sleepmgr_mode mode) +{ +#ifdef CONFIG_SLEEPMGR_ENABLE + irqflags_t flags; + + Assert (sleepmgr_locks[mode] < 0xff); + + // Enter a critical section + flags = cpu_irq_save (); + + ++sleepmgr_locks[mode]; + + // Leave the critical section + cpu_irq_restore (flags); +#else + UNUSED (mode); +#endif /* CONFIG_SLEEPMGR_ENABLE */ +} + +/** + * \brief Decrease lock count for a sleep mode + * + * Decreases the lock count for \a mode. If the lock count reaches 0, the sleep + * manager can put the device to sleep in the deeper sleep modes. + * + * \param mode Sleep mode to unlock. + */ +static inline void +sleepmgr_unlock_mode (enum sleepmgr_mode mode) +{ +#ifdef CONFIG_SLEEPMGR_ENABLE + irqflags_t flags; + + Assert (sleepmgr_locks[mode]); + + // Enter a critical section + flags = cpu_irq_save (); + + --sleepmgr_locks[mode]; + + // Leave the critical section + cpu_irq_restore (flags); +#else + UNUSED (mode); +#endif /* CONFIG_SLEEPMGR_ENABLE */ +} + + /** + * \brief Retrieves the deepest allowable sleep mode + * + * Searches through the sleep mode lock counts, starting at the shallowest sleep + * mode, until the first non-zero lock count is found. The deepest allowable + * sleep mode is then returned. + */ +static inline enum sleepmgr_mode +sleepmgr_get_sleep_mode (void) +{ + enum sleepmgr_mode sleep_mode = SLEEPMGR_ACTIVE; + +#ifdef CONFIG_SLEEPMGR_ENABLE + uint8_t *lock_ptr = sleepmgr_locks; + + // Find first non-zero lock count, starting with the shallowest modes. + while (!(*lock_ptr)) + { + lock_ptr++; + sleep_mode++; + } + + // Catch the case where one too many sleepmgr_unlock_mode() call has been + // performed on the deepest sleep mode. + Assert ((uintptr_t) (lock_ptr - sleepmgr_locks) < SLEEPMGR_NR_OF_MODES); + +#endif /* CONFIG_SLEEPMGR_ENABLE */ + + return sleep_mode; +} + +/** + * \fn sleepmgr_enter_sleep + * \brief Go to sleep in the deepest allowed mode + * + * Searches through the sleep mode lock counts, starting at the shallowest sleep + * mode, until the first non-zero lock count is found. The device is then put to + * sleep in the sleep mode that corresponds to the lock. + * + * \note This function enables interrupts before going to sleep, and will leave + * them enabled upon return. This also applies if sleep is skipped due to ACTIVE + * mode being locked. + */ + +static inline void +sleepmgr_enter_sleep (void) +{ +#ifdef CONFIG_SLEEPMGR_ENABLE + enum sleepmgr_mode sleep_mode; + + cpu_irq_disable (); + + // Find the deepest allowable sleep mode + sleep_mode = sleepmgr_get_sleep_mode (); + // Return right away if first mode (ACTIVE) is locked. + if (sleep_mode == SLEEPMGR_ACTIVE) + { + cpu_irq_enable (); + return; + } + // Enter the deepest allowable sleep mode with interrupts enabled + sleepmgr_sleep (sleep_mode); +#else + cpu_irq_enable (); +#endif /* CONFIG_SLEEPMGR_ENABLE */ +} + + +//! @} + +#endif /* SLEEPMGR_H */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/sleepmgr/xmega/sleepmgr.c b/bacnet-stack/ports/xplained/ASF/common/services/sleepmgr/xmega/sleepmgr.c new file mode 100644 index 00000000..004cdff9 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/sleepmgr/xmega/sleepmgr.c @@ -0,0 +1,58 @@ +/** + * \file + * + * \brief Sleep manager + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include +#include + +#if defined(CONFIG_SLEEPMGR_ENABLE) || defined(__DOXYGEN__) + +uint8_t sleepmgr_locks[SLEEPMGR_NR_OF_MODES]; + +enum SLEEP_SMODE_enum sleepmgr_configs[SLEEPMGR_NR_OF_MODES] = { + SLEEP_SMODE_IDLE_gc, + SLEEP_SMODE_ESTDBY_gc, + SLEEP_SMODE_PSAVE_gc, + SLEEP_SMODE_STDBY_gc, + SLEEP_SMODE_PDOWN_gc, +}; + +#endif /* CONFIG_SLEEPMGR_ENABLE */ diff --git a/bacnet-stack/ports/xplained/ASF/common/services/sleepmgr/xmega/sleepmgr.h b/bacnet-stack/ports/xplained/ASF/common/services/sleepmgr/xmega/sleepmgr.h new file mode 100644 index 00000000..c1583731 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/services/sleepmgr/xmega/sleepmgr.h @@ -0,0 +1,116 @@ +/** + * \file + * + * \brief AVR XMEGA Sleep manager implementation + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef XMEGA_SLEEPMGR_H +#define XMEGA_SLEEPMGR_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include + +/** + * \weakgroup sleepmgr_group + * @{ + */ + + enum sleepmgr_mode + { + //! Active mode. + SLEEPMGR_ACTIVE = 0, + //! Idle mode. + SLEEPMGR_IDLE, + //! Extended Standby mode. + SLEEPMGR_ESTDBY, + //! Power Save mode. + SLEEPMGR_PSAVE, + //! Standby mode. + SLEEPMGR_STDBY, + //! Power Down mode. + SLEEPMGR_PDOWN, + SLEEPMGR_NR_OF_MODES, + }; + +/** + * \internal + * \name Internal arrays + * @{ + */ +#if defined(CONFIG_SLEEPMGR_ENABLE) || defined(__DOXYGEN__) +//! Sleep mode lock counters + extern uint8_t sleepmgr_locks[]; +/** + * \brief Look-up table with sleep mode configurations + * \note This is located in program memory (Flash) as it is constant. + */ + extern enum SLEEP_SMODE_enum sleepmgr_configs[]; +#endif /* CONFIG_SLEEPMGR_ENABLE */ +//! @} + + static inline void sleepmgr_sleep (const enum sleepmgr_mode sleep_mode) + { + Assert (sleep_mode != SLEEPMGR_ACTIVE); +#ifdef CONFIG_SLEEPMGR_ENABLE + sleep_set_mode (sleepmgr_configs[sleep_mode - 1]); + sleep_enable (); + + cpu_irq_enable (); + sleep_enter (); + + sleep_disable (); +#else + cpu_irq_enable (); +#endif /* CONFIG_SLEEPMGR_ENABLE */ + + } + +//! @} + +#ifdef __cplusplus +} +#endif + +#endif /* XMEGA_SLEEPMGR_H */ diff --git a/bacnet-stack/ports/xplained/ASF/common/utils/interrupt.h b/bacnet-stack/ports/xplained/ASF/common/utils/interrupt.h new file mode 100644 index 00000000..63686515 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/utils/interrupt.h @@ -0,0 +1,139 @@ +/** + * \file + * + * \brief Global interrupt management for 8- and 32-bit AVR + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef UTILS_INTERRUPT_H +#define UTILS_INTERRUPT_H + +#include + +#if XMEGA || MEGA || TINY +# include "interrupt/interrupt_avr8.h" +#elif UC3 +# include "interrupt/interrupt_avr32.h" +#elif SAM3S || SAM3N || SAM3XA || SAM3U || SAM4S || SAM4L || SAM4E +# include "interrupt/interrupt_sam_nvic.h" +#else +# error Unsupported device. +#endif + +/** + * \defgroup interrupt_group Global interrupt management + * + * This is a driver for global enabling and disabling of interrupts. + * + * @{ + */ + +#if defined(__DOXYGEN__) +/** + * \def CONFIG_INTERRUPT_FORCE_INTC + * \brief Force usage of the ASF INTC driver + * + * Predefine this symbol when preprocessing to force the use of the ASF INTC driver. + * This is useful to ensure compatibilty accross compilers and shall be used only when required + * by the application needs. + */ +# define CONFIG_INTERRUPT_FORCE_INTC +#endif + +//! \name Global interrupt flags +//@{ +/** + * \typedef irqflags_t + * \brief Type used for holding state of interrupt flag + */ + +/** + * \def cpu_irq_enable + * \brief Enable interrupts globally + */ + +/** + * \def cpu_irq_disable + * \brief Disable interrupts globally + */ + +/** + * \fn irqflags_t cpu_irq_save(void) + * \brief Get and clear the global interrupt flags + * + * Use in conjunction with \ref cpu_irq_restore. + * + * \return Current state of interrupt flags. + * + * \note This function leaves interrupts disabled. + */ + +/** + * \fn void cpu_irq_restore(irqflags_t flags) + * \brief Restore global interrupt flags + * + * Use in conjunction with \ref cpu_irq_save. + * + * \param flags State to set interrupt flag to. + */ + +/** + * \fn bool cpu_irq_is_enabled_flags(irqflags_t flags) + * \brief Check if interrupts are globally enabled in supplied flags + * + * \param flags Currents state of interrupt flags. + * + * \return True if interrupts are enabled. + */ + +/** + * \def cpu_irq_is_enabled + * \brief Check if interrupts are globally enabled + * + * \return True if interrupts are enabled. + */ +//@} + +//! @} + +/** + * \ingroup interrupt_group + * \defgroup interrupt_deprecated_group Deprecated interrupt definitions + */ + +#endif /* UTILS_INTERRUPT_H */ diff --git a/bacnet-stack/ports/xplained/ASF/common/utils/interrupt/interrupt_avr8.h b/bacnet-stack/ports/xplained/ASF/common/utils/interrupt/interrupt_avr8.h new file mode 100644 index 00000000..802cb865 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/utils/interrupt/interrupt_avr8.h @@ -0,0 +1,148 @@ +/** + * \file + * + * \brief Global interrupt management for 8-bit AVR + * + * Copyright (C) 2010-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef UTILS_INTERRUPT_INTERRUPT_H +#define UTILS_INTERRUPT_INTERRUPT_H + +#include +#include + +/** + * \weakgroup interrupt_group + * + * @{ + */ + +#ifdef ISR_CUSTOM_H +# include ISR_CUSTOM_H +#else + +/** + * \def ISR + * \brief Define service routine for specified interrupt vector + * + * Usage: + * \code + * ISR(FOO_vect) + * { + * ... + * } + * \endcode + * + * \param vect Interrupt vector name as found in the device header files. + */ +#if defined(__DOXYGEN__) +# define ISR(vect) +#elif defined(__GNUC__) +# include +#elif defined(__ICCAVR__) +# define __ISR(x) _Pragma(#x) +# define ISR(vect) __ISR(vector=vect) __interrupt void handler_##vect(void) +#endif +#endif // ISR_CUSTOM_H + +#if XMEGA +/** + * \brief Initialize interrupt vectors + * Enables all interrupt levels, with vectors located in the application section + * and fixed priority scheduling. + */ +#define irq_initialize_vectors() \ + PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm; +#elif MEGA_RF +#define irq_initialize_vectors() +#endif + +#ifdef __GNUC__ +# define cpu_irq_enable() sei() +# define cpu_irq_disable() cli() +#else +# define cpu_irq_enable() __enable_interrupt() +# define cpu_irq_disable() __disable_interrupt() +#endif + +typedef uint8_t irqflags_t; + +static inline irqflags_t +cpu_irq_save (void) +{ + irqflags_t flags = SREG; + cpu_irq_disable (); + return flags; +} + +static inline void +cpu_irq_restore (irqflags_t flags) +{ + barrier (); + SREG = flags; +} + +static inline bool +cpu_irq_is_enabled_flags (irqflags_t flags) +{ +#if XMEGA +# ifdef __GNUC__ + return flags & CPU_I_bm; +# else + return flags & I_bm; +# endif +#elif MEGA || TINY + return flags & (1 << SREG_I); +#endif +} + +#define cpu_irq_is_enabled() cpu_irq_is_enabled_flags(SREG) + +//! @} + +/** + * \weakgroup interrupt_deprecated_group + * @{ + */ +// Deprecated definitions. +#define Enable_global_interrupt() cpu_irq_enable() +#define Disable_global_interrupt() cpu_irq_disable() +#define Is_global_interrupt_enabled() cpu_irq_is_enabled() +//! @} + +#endif /* UTILS_INTERRUPT_INTERRUPT_H */ diff --git a/bacnet-stack/ports/xplained/ASF/common/utils/make/Makefile.avr.in b/bacnet-stack/ports/xplained/ASF/common/utils/make/Makefile.avr.in new file mode 100644 index 00000000..5c73377e --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/utils/make/Makefile.avr.in @@ -0,0 +1,459 @@ +# List of available make goals: +# +# all Default target, builds the project +# clean Clean up the project +# rebuild Rebuild the project +# +# doc Build the documentation +# cleandoc Clean up the documentation +# rebuilddoc Rebuild the documentation +# +# +# Copyright (c) 2009-2012 Atmel Corporation. All rights reserved. +# +# \asf_license_start +# +# \page License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. The name of Atmel may not be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# 4. This software may only be redistributed and used in connection with an +# Atmel microcontroller product. +# +# THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE +# EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# \asf_license_stop +# + +# Include the config.mk file from the current working path, e.g., where the +# user called make. +include config.mk + +# Tool to use to generate documentation from the source code +DOCGEN ?= doxygen + +# Look for source files relative to the top-level source directory +VPATH := $(PRJ_PATH) + +# Output target file +target := $(TARGET) + +# Output project name (target name minus suffix) +project := $(basename $(target)) + +# Output target file (typically ELF or static library) +ifeq ($(suffix $(target)),.a) +target_type := lib +else +ifeq ($(suffix $(target)),.elf) +target_type := elf +else +$(error "Target type $(target_type) is not supported") +endif +endif + +# Allow override of operating system detection. The user can add OS=Linux or +# OS=Windows on the command line to explicit set the host OS. +# +# This allows to work around broken uname utility on certain systems. +ifdef OS + ifeq ($(strip $(OS)), Linux) + os_type := Linux + endif + ifeq ($(strip $(OS)), Windows) + os_type := windows32_64 + endif +endif + +os_type ?= $(strip $(shell uname)) + +ifeq ($(os_type),windows32) +os := Windows +else +ifeq ($(os_type),windows64) +os := Windows +else +ifeq ($(os_type),windows32_64) +os ?= Windows +else +ifeq ($(os_type),) +os := Windows +else +# Default to Linux style operating system. Both Cygwin and mingw are fully +# compatible (for this Makefile) with Linux. +os := Linux +endif +endif +endif +endif + +# Output documentation directory and configuration file. +docdir := ../doxygen/html +doccfg := ../doxygen/doxyfile.doxygen + +CROSS ?= avr- +AR := $(CROSS)ar +AS := $(CROSS)as +CC := $(CROSS)gcc +CPP := $(CROSS)gcc -E +CXX := $(CROSS)g++ +LD := $(CROSS)g++ +NM := $(CROSS)nm +OBJCOPY := $(CROSS)objcopy +OBJDUMP := $(CROSS)objdump +SIZE := $(CROSS)size + +RM := rm +ifeq ($(os),Windows) +RMDIR := rmdir /S /Q +else +RMDIR := rmdir -p --ignore-fail-on-non-empty +endif + +# Strings for beautifying output +MSG_CLEAN_FILES = "RM *.o *.d" +MSG_CLEAN_DIRS = "RMDIR $(strip $(clean-dirs))" +MSG_CLEAN_DOC = "RMDIR $(docdir)" +MSG_MKDIR = "MKDIR $(dir $@)" + +MSG_INFO = "INFO " + +MSG_ARCHIVING = "AR $@" +MSG_ASSEMBLING = "AS $@" +MSG_BINARY_IMAGE = "OBJCOPY $@" +MSG_COMPILING = "CC $@" +MSG_COMPILING_CXX = "CXX $@" +MSG_EEPROM_IMAGE = "OBJCOPY $@" +MSG_EXTENDED_LISTING = "OBJDUMP $@" +MSG_IHEX_IMAGE = "OBJCOPY $@" +MSG_LINKING = "LN $@" +MSG_PREPROCESSING = "CPP $@" +MSG_SIZE = "SIZE $@" +MSG_SYMBOL_TABLE = "NM $@" + +MSG_GENERATING_DOC = "DOXYGEN $(docdir)" + +# Don't use make's built-in rules and variables +MAKEFLAGS += -rR + +# Don't print 'Entering directory ...' +MAKEFLAGS += --no-print-directory + +# Function for reversing the order of a list +reverse = $(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) + +# Hide command output by default, but allow the user to override this +# by adding V=1 on the command line. +# +# This is inspired by the Kbuild system used by the Linux kernel. +ifdef V + ifeq ("$(origin V)", "command line") + VERBOSE = $(V) + endif +endif +ifndef VERBOSE + VERBOSE = 0 +endif + +ifeq ($(VERBOSE), 1) + Q = +else + Q = @ +endif + +arflags-gnu-y := $(ARFLAGS) +asflags-gnu-y := $(ASFLAGS) +cflags-gnu-y := $(CFLAGS) +cxxflags-gnu-y := $(CXXFLAGS) +cppflags-gnu-y := $(CPPFLAGS) +cpuflags-gnu-y := +dbgflags-gnu-y := $(DBGFLAGS) +libflags-gnu-y := $(foreach LIB,$(LIBS),-l$(LIB)) +ldflags-gnu-y := $(LDFLAGS) +flashflags-gnu-y := $(FLASHFLAGS) +eepromflags-gnu-y := $(EEPROMFLAGS) +clean-files := +clean-dirs := + +clean-files += $(wildcard $(target) $(project).map) +clean-files += $(wildcard $(project).hex $(project).eep) +clean-files += $(wildcard $(project).lss $(project).sym) +clean-files += $(wildcard $(build)) + +# Use pipes instead of temporary files for communication between processes +cflags-gnu-y += -pipe +asflags-gnu-y += -pipe +ldflags-gnu-y += -pipe + +# Archiver flags. +arflags-gnu-y += rcs + +# Always enable warnings. And be very careful about implicit +# declarations. +cflags-gnu-y += -Wall -Wstrict-prototypes -Wmissing-prototypes +cflags-gnu-y += -Werror-implicit-function-declaration +cxxflags-gnu-y += -Wall +# IAR doesn't allow arithmetic on void pointers, so warn about that. +cflags-gnu-y += -Wpointer-arith +cxxflags-gnu-y += -Wpointer-arith + +# Preprocessor flags. +cppflags-gnu-y += $(foreach INC,$(addprefix $(PRJ_PATH)/,$(INC_PATH)),-I$(INC)) +asflags-gnu-y += $(foreach INC,$(addprefix $(PRJ_PATH)/,$(INC_PATH)),'-Wa,-I$(INC)') + +# CPU specific flags. +cpuflags-gnu-y += -mmcu=$(MCU) + +# Dependency file flags. +depflags = -MD -MP -MQ $@ + +# Debug specific flags. +ifdef BUILD_DEBUG_LEVEL +dbgflags-gnu-y += -g$(BUILD_DEBUG_LEVEL) +else +dbgflags-gnu-y += -gdwarf-2 +endif + +# Optimization specific flags. +ifdef BUILD_OPTIMIZATION +optflags-gnu-y = -O$(BUILD_OPTIMIZATION) +else +optflags-gnu-y = $(OPTIMIZATION) +endif + +# Relax compilation and linking. +cflags-gnu-y += -mrelax +cxxflags-gnu-y += -mrelax +asflags-gnu-y += -mrelax +ldflags-gnu-y += -Wl,--relax + +# Always preprocess assembler files. +asflags-gnu-y += -x assembler-with-cpp +# Compile C files using the GNU99 standard. +cflags-gnu-y += -std=gnu99 +# Compile C++ files using the GNU++98 standard. +cxxflags-gnu-y += -std=gnu++98 + +# Use unsigned character type when compiling. +cflags-gnu-y += -funsigned-char +cxxflags-gnu-y += -funsigned-char + +# Don't use strict aliasing (very common in embedded applications). +cflags-gnu-y += -fno-strict-aliasing +cxxflags-gnu-y += -fno-strict-aliasing + +# Separate each function and data into its own separate section to allow +# garbage collection of unused sections. +cflags-gnu-y += -ffunction-sections -fdata-sections +cxxflags-gnu-y += -ffunction-sections -fdata-sections + +# Garbage collect unreferred sections when linking. +ldflags-gnu-y += -Wl,--gc-sections + +# Output a link map file and a cross reference table +ldflags-gnu-y += -Wl,-Map=$(project).map,--cref + +# Add library search paths relative to the top level directory. +ldflags-gnu-y += $(foreach _LIB_PATH,$(addprefix $(PRJ_PATH)/,$(LIB_PATH)),-L$(_LIB_PATH)) + +a_flags = $(cpuflags-gnu-y) $(depflags) $(cppflags-gnu-y) $(asflags-gnu-y) -D__ASSEMBLY__ +c_flags = $(cpuflags-gnu-y) $(dbgflags-gnu-y) $(depflags) $(optflags-gnu-y) $(cppflags-gnu-y) $(cflags-gnu-y) +cxx_flags= $(cpuflags-gnu-y) $(dbgflags-gnu-y) $(depflags) $(optflags-gnu-y) $(cppflags-gnu-y) $(cxxflags-gnu-y) +l_flags = $(cpuflags-gnu-y) $(optflags-gnu-y) $(ldflags-gnu-y) +ar_flags = $(arflags-gnu-y) + +# Intel Hex file production flags +flashflags-gnu-y += -R .eeprom -R .usb_descriptor_table + +# Eeprom file production flags +eepromflags-gnu-y += -j .eeprom +eepromflags-gnu-y += --set-section-flags=.eeprom="alloc,load" +eepromflags-gnu-y += --change-section-lma .eeprom=0 + +# Source files list and part informations must already be included before +# running this makefile + +# If a custom build directory is specified, use it -- force trailing / in directory name. +ifdef BUILD_DIR + build-dir := $(dir $(BUILD_DIR))$(if $(notdir $(BUILD_DIR)),$(notdir $(BUILD_DIR))/) +else + build-dir = +endif + +# Create object files list from source files list. +obj-y := $(addprefix $(build-dir), $(addsuffix .o,$(basename $(CSRCS) $(ASSRCS)))) + +# Create dependency files list from source files list. +dep-files := $(wildcard $(foreach f,$(obj-y),$(basename $(f)).d)) + +clean-files += $(wildcard $(obj-y)) +clean-files += $(dep-files) + +clean-dirs += $(call reverse,$(sort $(wildcard $(dir $(obj-y))))) + +# Default target. +.PHONY: all +ifeq ($(target_type),lib) +all: $(target) $(project).lss $(project).sym +else +ifeq ($(target_type),elf) +all: $(target) $(project).hex $(project).lss $(project).sym +endif +endif + +# Clean up the project. +.PHONY: clean +clean: + @$(if $(strip $(clean-files)),echo $(MSG_CLEAN_FILES)) + $(if $(strip $(clean-files)),$(Q)$(RM) $(clean-files),) + @$(if $(strip $(clean-dirs)),echo $(MSG_CLEAN_DIRS)) +# Remove created directories, and make sure we only remove existing +# directories, since recursive rmdir might help us a bit on the way. +ifeq ($(os),Windows) + $(Q)$(if $(strip $(clean-dirs)), \ + $(RMDIR) $(strip $(subst /,\,$(clean-dirs)))) +else + $(Q)$(if $(strip $(clean-dirs)), \ + for directory in $(strip $(clean-dirs)); do \ + if [ -d "$$directory" ]; then \ + $(RMDIR) $$directory; \ + fi \ + done \ + ) +endif + +# Rebuild the project. +.PHONY: rebuild +rebuild: clean all + +.PHONY: objfiles +objfiles: $(obj-y) + +# Create object files from C source files. +$(build-dir)%.o: %.c $(MAKEFILE_PATH) config.mk + $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) +ifeq ($(os),Windows) + $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) +else + $(Q)test -d $(dir $@) || mkdir -p $(dir $@) +endif + @echo $(MSG_COMPILING) + $(Q)$(CC) $(c_flags) -c $< -o $@ + +# Create object files from C++ source files. +$(build-dir)%.o: %.cpp $(MAKEFILE_PATH) config.mk + $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) +ifeq ($(os),Windows) + $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) +else + $(Q)test -d $(dir $@) || mkdir -p $(dir $@) +endif + @echo $(MSG_COMPILING_CXX) + $(Q)$(CXX) $(cxx_flags) -c $< -o $@ + +# Preprocess and assemble: create object files from assembler source files. +$(build-dir)%.o: %.s $(MAKEFILE_PATH) config.mk + $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) +ifeq ($(os),Windows) + $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) +else + $(Q)test -d $(dir $@) || mkdir -p $(dir $@) +endif + @echo $(MSG_ASSEMBLING) + $(Q)$(CC) $(a_flags) -c $< -o $@ + +# Preprocess and assemble: create object files from assembler source files. +$(build-dir)%.o: %.S $(MAKEFILE_PATH) config.mk + $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) +ifeq ($(os),Windows) + $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) +else + $(Q)test -d $(dir $@) || mkdir -p $(dir $@) +endif + @echo $(MSG_ASSEMBLING) + $(Q)$(CC) $(a_flags) -c $< -o $@ + +# Include all dependency files to add depedency to all header files in use. +include $(dep-files) + +ifeq ($(target_type),lib) +# Archive object files into an archive +$(target): $(MAKEFILE_PATH) config.mk $(obj-y) + @echo $(MSG_ARCHIVING) + $(Q)$(AR) $(ar_flags) $@ $(obj-y) + @echo $(MSG_SIZE) + $(Q)$(SIZE) -Bxt $@ +else +ifeq ($(target_type),elf) +# Link the object files into an ELF file. Also make sure the target is rebuilt +# if the common Makefile.avr.in or project config.mk is changed. +$(target): $(MAKEFILE_PATH) config.mk $(obj-y) + @echo $(MSG_LINKING) + $(Q)$(CC) $(l_flags) $(obj-y) $(libflags-gnu-y) -o $@ + @echo $(MSG_SIZE) + $(Q)$(SIZE) -Ax $@ + $(Q)$(SIZE) -Bx $@ +endif +endif + +# Create extended function listing from target output file. +%.lss: $(target) + @echo $(MSG_EXTENDED_LISTING) + $(Q)$(OBJDUMP) -h -S $< > $@ + +# Create symbol table from target output file. +%.sym: $(target) + @echo $(MSG_SYMBOL_TABLE) + $(Q)$(NM) -n $< > $@ + +# Create Intel HEX image from ELF output file. +%.hex: $(target) + @echo $(MSG_IHEX_IMAGE) + $(Q)$(OBJCOPY) -O ihex $(flashflags-gnu-y) $< $@ + +# Create EEPROM Intel HEX image from ELF output file. +%.eep: $(target) + @echo $(MSG_EEPROM_IMAGE) + $(Q)$(OBJCOPY) $(eepromflags-gnu-y) -O ihex $< $@ || exit 0 + +# Provide information about the detected host operating system. +.SECONDARY: info-os +info-os: + @echo $(MSG_INFO)$(os) build host detected + +# Build Doxygen generated documentation. +.PHONY: doc +doc: + @echo $(MSG_GENERATING_DOC) + $(Q)cd $(dir $(doccfg)) && $(DOCGEN) $(notdir $(doccfg)) + +# Clean Doxygen generated documentation. +.PHONY: cleandoc +cleandoc: + @$(if $(wildcard $(docdir)),echo $(MSG_CLEAN_DOC)) + $(Q)$(if $(wildcard $(docdir)),$(RM) --recursive $(docdir)) + +# Rebuild the Doxygen generated documentation. +.PHONY: rebuilddoc +rebuilddoc: cleandoc doc diff --git a/bacnet-stack/ports/xplained/ASF/common/utils/parts.h b/bacnet-stack/ports/xplained/ASF/common/utils/parts.h new file mode 100644 index 00000000..0ca16f92 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/utils/parts.h @@ -0,0 +1,889 @@ +/** + * \file + * + * \brief Atmel part identification macros + * + * Copyright (C) 2012-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#ifndef ATMEL_PARTS_H +#define ATMEL_PARTS_H + +/** + * \defgroup part_macros_group Atmel part identification macros + * + * This collection of macros identify which series and families that the various + * Atmel parts belong to. These can be used to select part-dependent sections of + * code at compile time. + * + * @{ + */ + +/** + * \name Convenience macros for part checking + * @{ + */ +/* ! Check GCC and IAR part definition for 8-bit AVR */ +#define AVR8_PART_IS_DEFINED(part) \ + (defined(__ ## part ## __) || defined(__AVR_ ## part ## __)) + +/* ! Check GCC and IAR part definition for 32-bit AVR */ +#define AVR32_PART_IS_DEFINED(part) \ + (defined(__AT32 ## part ## __) || defined(__AVR32_ ## part ## __)) + +/* ! Check GCC and IAR part definition for SAM */ +#define SAM_PART_IS_DEFINED(part) (defined(__ ## part ## __)) +/** @} */ + +/** + * \defgroup uc3_part_macros_group AVR UC3 parts + * @{ + */ + +/** + * \name AVR UC3 A series + * @{ + */ +#define UC3A0 ( \ + AVR32_PART_IS_DEFINED(UC3A0128) || \ + AVR32_PART_IS_DEFINED(UC3A0256) || \ + AVR32_PART_IS_DEFINED(UC3A0512) \ + ) + +#define UC3A1 ( \ + AVR32_PART_IS_DEFINED(UC3A1128) || \ + AVR32_PART_IS_DEFINED(UC3A1256) || \ + AVR32_PART_IS_DEFINED(UC3A1512) \ + ) + +#define UC3A3 ( \ + AVR32_PART_IS_DEFINED(UC3A364) || \ + AVR32_PART_IS_DEFINED(UC3A364S) || \ + AVR32_PART_IS_DEFINED(UC3A3128) || \ + AVR32_PART_IS_DEFINED(UC3A3128S) || \ + AVR32_PART_IS_DEFINED(UC3A3256) || \ + AVR32_PART_IS_DEFINED(UC3A3256S) \ + ) + +#define UC3A4 ( \ + AVR32_PART_IS_DEFINED(UC3A464) || \ + AVR32_PART_IS_DEFINED(UC3A464S) || \ + AVR32_PART_IS_DEFINED(UC3A4128) || \ + AVR32_PART_IS_DEFINED(UC3A4128S) || \ + AVR32_PART_IS_DEFINED(UC3A4256) || \ + AVR32_PART_IS_DEFINED(UC3A4256S) \ + ) +/** @} */ + +/** + * \name AVR UC3 B series + * @{ + */ +#define UC3B0 ( \ + AVR32_PART_IS_DEFINED(UC3B064) || \ + AVR32_PART_IS_DEFINED(UC3B0128) || \ + AVR32_PART_IS_DEFINED(UC3B0256) || \ + AVR32_PART_IS_DEFINED(UC3B0512) \ + ) + +#define UC3B1 ( \ + AVR32_PART_IS_DEFINED(UC3B164) || \ + AVR32_PART_IS_DEFINED(UC3B1128) || \ + AVR32_PART_IS_DEFINED(UC3B1256) || \ + AVR32_PART_IS_DEFINED(UC3B1512) \ + ) +/** @} */ + +/** + * \name AVR UC3 C series + * @{ + */ +#define UC3C0 ( \ + AVR32_PART_IS_DEFINED(UC3C064C) || \ + AVR32_PART_IS_DEFINED(UC3C0128C) || \ + AVR32_PART_IS_DEFINED(UC3C0256C) || \ + AVR32_PART_IS_DEFINED(UC3C0512C) \ + ) + +#define UC3C1 ( \ + AVR32_PART_IS_DEFINED(UC3C164C) || \ + AVR32_PART_IS_DEFINED(UC3C1128C) || \ + AVR32_PART_IS_DEFINED(UC3C1256C) || \ + AVR32_PART_IS_DEFINED(UC3C1512C) \ + ) + +#define UC3C2 ( \ + AVR32_PART_IS_DEFINED(UC3C264C) || \ + AVR32_PART_IS_DEFINED(UC3C2128C) || \ + AVR32_PART_IS_DEFINED(UC3C2256C) || \ + AVR32_PART_IS_DEFINED(UC3C2512C) \ + ) +/** @} */ + +/** + * \name AVR UC3 D series + * @{ + */ +#define UC3D3 ( \ + AVR32_PART_IS_DEFINED(UC64D3) || \ + AVR32_PART_IS_DEFINED(UC128D3) \ + ) + +#define UC3D4 ( \ + AVR32_PART_IS_DEFINED(UC64D4) || \ + AVR32_PART_IS_DEFINED(UC128D4) \ + ) +/** @} */ + +/** + * \name AVR UC3 L series + * @{ + */ +#define UC3L0 ( \ + AVR32_PART_IS_DEFINED(UC3L016) || \ + AVR32_PART_IS_DEFINED(UC3L032) || \ + AVR32_PART_IS_DEFINED(UC3L064) \ + ) + +#define UC3L0128 ( \ + AVR32_PART_IS_DEFINED(UC3L0128) \ + ) + +#define UC3L0256 ( \ + AVR32_PART_IS_DEFINED(UC3L0256) \ + ) + +#define UC3L3 ( \ + AVR32_PART_IS_DEFINED(UC64L3U) || \ + AVR32_PART_IS_DEFINED(UC128L3U) || \ + AVR32_PART_IS_DEFINED(UC256L3U) \ + ) + +#define UC3L4 ( \ + AVR32_PART_IS_DEFINED(UC64L4U) || \ + AVR32_PART_IS_DEFINED(UC128L4U) || \ + AVR32_PART_IS_DEFINED(UC256L4U) \ + ) + +#define UC3L3_L4 (UC3L3 || UC3L4) +/** @} */ + +/** + * \name AVR UC3 families + * @{ + */ +/** AVR UC3 A family */ +#define UC3A (UC3A0 || UC3A1 || UC3A3 || UC3A4) + +/** AVR UC3 B family */ +#define UC3B (UC3B0 || UC3B1) + +/** AVR UC3 C family */ +#define UC3C (UC3C0 || UC3C1 || UC3C2) + +/** AVR UC3 D family */ +#define UC3D (UC3D3 || UC3D4) + +/** AVR UC3 L family */ +#define UC3L (UC3L0 || UC3L0128 || UC3L0256 || UC3L3_L4) +/** @} */ + +/** AVR UC3 product line */ +#define UC3 (UC3A || UC3B || UC3C || UC3D || UC3L) + +/** @} */ + +/** + * \defgroup xmega_part_macros_group AVR XMEGA parts + * @{ + */ + +/** + * \name AVR XMEGA A series + * @{ + */ +#define XMEGA_A1 ( \ + AVR8_PART_IS_DEFINED(ATxmega64A1) || \ + AVR8_PART_IS_DEFINED(ATxmega128A1) \ + ) + +#define XMEGA_A3 ( \ + AVR8_PART_IS_DEFINED(ATxmega64A3) || \ + AVR8_PART_IS_DEFINED(ATxmega128A3) || \ + AVR8_PART_IS_DEFINED(ATxmega192A3) || \ + AVR8_PART_IS_DEFINED(ATxmega256A3) \ + ) + +#define XMEGA_A3B ( \ + AVR8_PART_IS_DEFINED(ATxmega256A3B) \ + ) + +#define XMEGA_A4 ( \ + AVR8_PART_IS_DEFINED(ATxmega16A4) || \ + AVR8_PART_IS_DEFINED(ATxmega32A4) \ + ) +/** @} */ + +/** + * \name AVR XMEGA AU series + * @{ + */ +#define XMEGA_A1U ( \ + AVR8_PART_IS_DEFINED(ATxmega64A1U) || \ + AVR8_PART_IS_DEFINED(ATxmega128A1U) \ + ) + +#define XMEGA_A3U ( \ + AVR8_PART_IS_DEFINED(ATxmega64A3U) || \ + AVR8_PART_IS_DEFINED(ATxmega128A3U) || \ + AVR8_PART_IS_DEFINED(ATxmega192A3U) || \ + AVR8_PART_IS_DEFINED(ATxmega256A3U) \ + ) + +#define XMEGA_A3BU ( \ + AVR8_PART_IS_DEFINED(ATxmega256A3BU) \ + ) + +#define XMEGA_A4U ( \ + AVR8_PART_IS_DEFINED(ATxmega16A4U) || \ + AVR8_PART_IS_DEFINED(ATxmega32A4U) || \ + AVR8_PART_IS_DEFINED(ATxmega64A4U) || \ + AVR8_PART_IS_DEFINED(ATxmega128A4U) \ + ) +/** @} */ + +/** + * \name AVR XMEGA B series + * @{ + */ +#define XMEGA_B1 ( \ + AVR8_PART_IS_DEFINED(ATxmega64B1) || \ + AVR8_PART_IS_DEFINED(ATxmega128B1) \ + ) + +#define XMEGA_B3 ( \ + AVR8_PART_IS_DEFINED(ATxmega64B3) || \ + AVR8_PART_IS_DEFINED(ATxmega128B3) \ + ) +/** @} */ + +/** + * \name AVR XMEGA C series + * @{ + */ +#define XMEGA_C3 ( \ + AVR8_PART_IS_DEFINED(ATxmega384C3) || \ + AVR8_PART_IS_DEFINED(ATxmega256C3) || \ + AVR8_PART_IS_DEFINED(ATxmega192C3) || \ + AVR8_PART_IS_DEFINED(ATxmega128C3) || \ + AVR8_PART_IS_DEFINED(ATxmega64C3) \ + ) + +#define XMEGA_C4 ( \ + AVR8_PART_IS_DEFINED(ATxmega32C4) || \ + AVR8_PART_IS_DEFINED(ATxmega16C4) \ + ) +/** @} */ + +/** + * \name AVR XMEGA D series + * @{ + */ +#define XMEGA_D3 ( \ + AVR8_PART_IS_DEFINED(ATxmega64D3) || \ + AVR8_PART_IS_DEFINED(ATxmega128D3) || \ + AVR8_PART_IS_DEFINED(ATxmega192D3) || \ + AVR8_PART_IS_DEFINED(ATxmega256D3) || \ + AVR8_PART_IS_DEFINED(ATxmega384D3) \ + ) + +#define XMEGA_D4 ( \ + AVR8_PART_IS_DEFINED(ATxmega16D4) || \ + AVR8_PART_IS_DEFINED(ATxmega32D4) || \ + AVR8_PART_IS_DEFINED(ATxmega64D4) || \ + AVR8_PART_IS_DEFINED(ATxmega128D4) \ + ) +/** @} */ + +/** + * \name AVR XMEGA E series + * @{ + */ +#define XMEGA_E5 ( \ + AVR8_PART_IS_DEFINED(ATxmega8E5) || \ + AVR8_PART_IS_DEFINED(ATxmega16E5) || \ + AVR8_PART_IS_DEFINED(ATxmega32E5) \ + ) +/** @} */ + + +/** + * \name AVR XMEGA families + * @{ + */ +/** AVR XMEGA A family */ +#define XMEGA_A (XMEGA_A1 || XMEGA_A3 || XMEGA_A3B || XMEGA_A4) + +/** AVR XMEGA AU family */ +#define XMEGA_AU (XMEGA_A1U || XMEGA_A3U || XMEGA_A3BU || XMEGA_A4U) + +/** AVR XMEGA B family */ +#define XMEGA_B (XMEGA_B1 || XMEGA_B3) + +/** AVR XMEGA C family */ +#define XMEGA_C (XMEGA_C3 || XMEGA_C4) + +/** AVR XMEGA D family */ +#define XMEGA_D (XMEGA_D3 || XMEGA_D4) + +/** AVR XMEGA E family */ +#define XMEGA_E (XMEGA_E5) +/** @} */ + + +/** AVR XMEGA product line */ +#define XMEGA (XMEGA_A || XMEGA_AU || XMEGA_B || XMEGA_C || XMEGA_D || XMEGA_E) + +/** @} */ + +/** + * \defgroup mega_part_macros_group megaAVR parts + * + * \note These megaAVR groupings are based on the groups in AVR Libc for the + * part header files. They are not names of official megaAVR device series or + * families. + * + * @{ + */ + +/** + * \name ATmegaxx0/xx1 subgroups + * @{ + */ +#define MEGA_XX0 ( \ + AVR8_PART_IS_DEFINED(ATmega640) || \ + AVR8_PART_IS_DEFINED(ATmega1280) || \ + AVR8_PART_IS_DEFINED(ATmega2560) \ + ) + +#define MEGA_XX1 ( \ + AVR8_PART_IS_DEFINED(ATmega1281) || \ + AVR8_PART_IS_DEFINED(ATmega2561) \ + ) +/** @} */ + +/** + * \name megaAVR groups + * @{ + */ +/** ATmegaxx0/xx1 group */ +#define MEGA_XX0_1 (MEGA_XX0 || MEGA_XX1) + +/** ATmegaxx4 group */ +#define MEGA_XX4 ( \ + AVR8_PART_IS_DEFINED(ATmega164A) || \ + AVR8_PART_IS_DEFINED(ATmega164PA) || \ + AVR8_PART_IS_DEFINED(ATmega324A) || \ + AVR8_PART_IS_DEFINED(ATmega324PA) || \ + AVR8_PART_IS_DEFINED(ATmega644) || \ + AVR8_PART_IS_DEFINED(ATmega644A) || \ + AVR8_PART_IS_DEFINED(ATmega644PA) || \ + AVR8_PART_IS_DEFINED(ATmega1284P) || \ + AVR8_PART_IS_DEFINED(ATmega128RFA1) \ + ) + +/** ATmegaxx4 group */ +#define MEGA_XX4_A ( \ + AVR8_PART_IS_DEFINED(ATmega164A) || \ + AVR8_PART_IS_DEFINED(ATmega164PA) || \ + AVR8_PART_IS_DEFINED(ATmega324A) || \ + AVR8_PART_IS_DEFINED(ATmega324PA) || \ + AVR8_PART_IS_DEFINED(ATmega644A) || \ + AVR8_PART_IS_DEFINED(ATmega644PA) || \ + AVR8_PART_IS_DEFINED(ATmega1284P) \ + ) + +/** ATmegaxx8 group */ +#define MEGA_XX8 ( \ + AVR8_PART_IS_DEFINED(ATmega48) || \ + AVR8_PART_IS_DEFINED(ATmega48A) || \ + AVR8_PART_IS_DEFINED(ATmega48PA) || \ + AVR8_PART_IS_DEFINED(ATmega88) || \ + AVR8_PART_IS_DEFINED(ATmega88A) || \ + AVR8_PART_IS_DEFINED(ATmega88PA) || \ + AVR8_PART_IS_DEFINED(ATmega168) || \ + AVR8_PART_IS_DEFINED(ATmega168A) || \ + AVR8_PART_IS_DEFINED(ATmega168PA) || \ + AVR8_PART_IS_DEFINED(ATmega328) || \ + AVR8_PART_IS_DEFINED(ATmega328P) \ + ) + +/** ATmegaxx8A/P/PA group */ +#define MEGA_XX8_A ( \ + AVR8_PART_IS_DEFINED(ATmega48A) || \ + AVR8_PART_IS_DEFINED(ATmega48PA) || \ + AVR8_PART_IS_DEFINED(ATmega88A) || \ + AVR8_PART_IS_DEFINED(ATmega88PA) || \ + AVR8_PART_IS_DEFINED(ATmega168A) || \ + AVR8_PART_IS_DEFINED(ATmega168PA) || \ + AVR8_PART_IS_DEFINED(ATmega328P) \ + ) + +/** ATmegaxx group */ +#define MEGA_XX ( \ + AVR8_PART_IS_DEFINED(ATmega16) || \ + AVR8_PART_IS_DEFINED(ATmega16A) || \ + AVR8_PART_IS_DEFINED(ATmega32) || \ + AVR8_PART_IS_DEFINED(ATmega32A) || \ + AVR8_PART_IS_DEFINED(ATmega64) || \ + AVR8_PART_IS_DEFINED(ATmega64A) || \ + AVR8_PART_IS_DEFINED(ATmega128) || \ + AVR8_PART_IS_DEFINED(ATmega128A) \ + ) + +/** ATmegaxxA/P/PA group */ +#define MEGA_XX_A ( \ + AVR8_PART_IS_DEFINED(ATmega16A) || \ + AVR8_PART_IS_DEFINED(ATmega32A) || \ + AVR8_PART_IS_DEFINED(ATmega64A) || \ + AVR8_PART_IS_DEFINED(ATmega128A) \ + ) +/** ATmegaxxRFA1 group */ +#define MEGA_RFA1 ( \ + AVR8_PART_IS_DEFINED(ATmega128RFA1) \ + ) + +/** ATmegaxxRFR2 group */ +#define MEGA_RFR2 ( \ + AVR8_PART_IS_DEFINED(ATmega64RFR2) || \ + AVR8_PART_IS_DEFINED(ATmega128RFR2) || \ + AVR8_PART_IS_DEFINED(ATmega256RFR2) \ + ) + +/** ATmegaxxRFxx group */ +#define MEGA_RF (MEGA_RFA1 || MEGA_RFR2) + +/** + * \name ATmegaxx_un0/un1/un2 subgroups + * @{ + */ +#define MEGA_XX_UN0 ( \ + AVR8_PART_IS_DEFINED(ATmega16) || \ + AVR8_PART_IS_DEFINED(ATmega16A) || \ + AVR8_PART_IS_DEFINED(ATmega32) || \ + AVR8_PART_IS_DEFINED(ATmega32A) \ + ) + +/** ATmegaxx group without power reduction and + * And interrupt sense register. + */ +#define MEGA_XX_UN1 ( \ + AVR8_PART_IS_DEFINED(ATmega64) || \ + AVR8_PART_IS_DEFINED(ATmega64A) || \ + AVR8_PART_IS_DEFINED(ATmega128) || \ + AVR8_PART_IS_DEFINED(ATmega128A) \ + ) + +/** ATmegaxx group without power reduction and + * And interrupt sense register. + */ +#define MEGA_XX_UN2 ( \ + AVR8_PART_IS_DEFINED(ATmega169P) || \ + AVR8_PART_IS_DEFINED(ATmega169PA) || \ + AVR8_PART_IS_DEFINED(ATmega329P) || \ + AVR8_PART_IS_DEFINED(ATmega329PA) \ + ) + +/** Devices added to complete megaAVR offering. + * Please do not use this group symbol as it is not intended + * to be permanent: the devices should be regrouped. + */ +#define MEGA_UNCATEGORIZED ( \ + AVR8_PART_IS_DEFINED(AT90CAN128) || \ + AVR8_PART_IS_DEFINED(AT90CAN32) || \ + AVR8_PART_IS_DEFINED(AT90CAN64) || \ + AVR8_PART_IS_DEFINED(AT90PWM1) || \ + AVR8_PART_IS_DEFINED(AT90PWM216) || \ + AVR8_PART_IS_DEFINED(AT90PWM2B) || \ + AVR8_PART_IS_DEFINED(AT90PWM316) || \ + AVR8_PART_IS_DEFINED(AT90PWM3B) || \ + AVR8_PART_IS_DEFINED(AT90PWM81) || \ + AVR8_PART_IS_DEFINED(AT90USB1286) || \ + AVR8_PART_IS_DEFINED(AT90USB1287) || \ + AVR8_PART_IS_DEFINED(AT90USB162) || \ + AVR8_PART_IS_DEFINED(AT90USB646) || \ + AVR8_PART_IS_DEFINED(AT90USB647) || \ + AVR8_PART_IS_DEFINED(AT90USB82) || \ + AVR8_PART_IS_DEFINED(ATmega1284) || \ + AVR8_PART_IS_DEFINED(ATmega162) || \ + AVR8_PART_IS_DEFINED(ATmega164P) || \ + AVR8_PART_IS_DEFINED(ATmega165A) || \ + AVR8_PART_IS_DEFINED(ATmega165P) || \ + AVR8_PART_IS_DEFINED(ATmega165PA) || \ + AVR8_PART_IS_DEFINED(ATmega168P) || \ + AVR8_PART_IS_DEFINED(ATmega169A) || \ + AVR8_PART_IS_DEFINED(ATmega16M1) || \ + AVR8_PART_IS_DEFINED(ATmega16U2) || \ + AVR8_PART_IS_DEFINED(ATmega16U4) || \ + AVR8_PART_IS_DEFINED(ATmega2564RFR2) || \ + AVR8_PART_IS_DEFINED(ATmega256RFA2) || \ + AVR8_PART_IS_DEFINED(ATmega324P) || \ + AVR8_PART_IS_DEFINED(ATmega325) || \ + AVR8_PART_IS_DEFINED(ATmega3250) || \ + AVR8_PART_IS_DEFINED(ATmega3250A) || \ + AVR8_PART_IS_DEFINED(ATmega3250P) || \ + AVR8_PART_IS_DEFINED(ATmega3250PA) || \ + AVR8_PART_IS_DEFINED(ATmega325A) || \ + AVR8_PART_IS_DEFINED(ATmega325P) || \ + AVR8_PART_IS_DEFINED(ATmega325PA) || \ + AVR8_PART_IS_DEFINED(ATmega329) || \ + AVR8_PART_IS_DEFINED(ATmega3290) || \ + AVR8_PART_IS_DEFINED(ATmega3290A) || \ + AVR8_PART_IS_DEFINED(ATmega3290P) || \ + AVR8_PART_IS_DEFINED(ATmega3290PA) || \ + AVR8_PART_IS_DEFINED(ATmega329A) || \ + AVR8_PART_IS_DEFINED(ATmega32M1) || \ + AVR8_PART_IS_DEFINED(ATmega32U2) || \ + AVR8_PART_IS_DEFINED(ATmega32U4) || \ + AVR8_PART_IS_DEFINED(ATmega48P) || \ + AVR8_PART_IS_DEFINED(ATmega644P) || \ + AVR8_PART_IS_DEFINED(ATmega645) || \ + AVR8_PART_IS_DEFINED(ATmega6450) || \ + AVR8_PART_IS_DEFINED(ATmega6450A) || \ + AVR8_PART_IS_DEFINED(ATmega6450P) || \ + AVR8_PART_IS_DEFINED(ATmega645A) || \ + AVR8_PART_IS_DEFINED(ATmega645P) || \ + AVR8_PART_IS_DEFINED(ATmega649) || \ + AVR8_PART_IS_DEFINED(ATmega6490) || \ + AVR8_PART_IS_DEFINED(ATmega6490A) || \ + AVR8_PART_IS_DEFINED(ATmega6490P) || \ + AVR8_PART_IS_DEFINED(ATmega649A) || \ + AVR8_PART_IS_DEFINED(ATmega649P) || \ + AVR8_PART_IS_DEFINED(ATmega64M1) || \ + AVR8_PART_IS_DEFINED(ATmega64RFA2) || \ + AVR8_PART_IS_DEFINED(ATmega8) || \ + AVR8_PART_IS_DEFINED(ATmega8515) || \ + AVR8_PART_IS_DEFINED(ATmega8535) || \ + AVR8_PART_IS_DEFINED(ATmega88P) || \ + AVR8_PART_IS_DEFINED(ATmega8A) || \ + AVR8_PART_IS_DEFINED(ATmega8U2) \ + ) + +/** Unspecified group */ +#define MEGA_UNSPECIFIED (MEGA_XX_UN0 || MEGA_XX_UN1 || MEGA_XX_UN2 || \ + MEGA_UNCATEGORIZED) + +/** @} */ + +/** megaAVR product line */ +#define MEGA (MEGA_XX0_1 || MEGA_XX4 || MEGA_XX8 || MEGA_XX || MEGA_RF || \ + MEGA_UNSPECIFIED) + +/** @} */ + +/** + * \defgroup tiny_part_macros_group tinyAVR parts + * + * @{ + */ + +/** + * \name tinyAVR groups + * @{ + */ + +/** Devices added to complete tinyAVR offering. + * Please do not use this group symbol as it is not intended + * to be permanent: the devices should be regrouped. + */ +#define TINY_UNCATEGORIZED ( \ + AVR8_PART_IS_DEFINED(ATtiny10) || \ + AVR8_PART_IS_DEFINED(ATtiny13) || \ + AVR8_PART_IS_DEFINED(ATtiny13A) || \ + AVR8_PART_IS_DEFINED(ATtiny1634) || \ + AVR8_PART_IS_DEFINED(ATtiny167) || \ + AVR8_PART_IS_DEFINED(ATtiny20) || \ + AVR8_PART_IS_DEFINED(ATtiny2313) || \ + AVR8_PART_IS_DEFINED(ATtiny2313A) || \ + AVR8_PART_IS_DEFINED(ATtiny24) || \ + AVR8_PART_IS_DEFINED(ATtiny24A) || \ + AVR8_PART_IS_DEFINED(ATtiny25) || \ + AVR8_PART_IS_DEFINED(ATtiny26) || \ + AVR8_PART_IS_DEFINED(ATtiny261) || \ + AVR8_PART_IS_DEFINED(ATtiny261A) || \ + AVR8_PART_IS_DEFINED(ATtiny4) || \ + AVR8_PART_IS_DEFINED(ATtiny40) || \ + AVR8_PART_IS_DEFINED(ATtiny4313) || \ + AVR8_PART_IS_DEFINED(ATtiny43U) || \ + AVR8_PART_IS_DEFINED(ATtiny44) || \ + AVR8_PART_IS_DEFINED(ATtiny44A) || \ + AVR8_PART_IS_DEFINED(ATtiny45) || \ + AVR8_PART_IS_DEFINED(ATtiny461) || \ + AVR8_PART_IS_DEFINED(ATtiny461A) || \ + AVR8_PART_IS_DEFINED(ATtiny48) || \ + AVR8_PART_IS_DEFINED(ATtiny5) || \ + AVR8_PART_IS_DEFINED(ATtiny828) || \ + AVR8_PART_IS_DEFINED(ATtiny84) || \ + AVR8_PART_IS_DEFINED(ATtiny84A) || \ + AVR8_PART_IS_DEFINED(ATtiny85) || \ + AVR8_PART_IS_DEFINED(ATtiny861) || \ + AVR8_PART_IS_DEFINED(ATtiny861A) || \ + AVR8_PART_IS_DEFINED(ATtiny87) || \ + AVR8_PART_IS_DEFINED(ATtiny88) || \ + AVR8_PART_IS_DEFINED(ATtiny9) \ + ) + +/** @} */ + +/** tinyAVR product line */ +#define TINY (TINY_UNCATEGORIZED) + +/** @} */ + +/** + * \defgroup sam_part_macros_group SAM parts + * @{ + */ + +/** + * \name SAM3S series + * @{ + */ +#define SAM3S1 ( \ + SAM_PART_IS_DEFINED(SAM3S1A) || \ + SAM_PART_IS_DEFINED(SAM3S1B) || \ + SAM_PART_IS_DEFINED(SAM3S1C) \ + ) + +#define SAM3S2 ( \ + SAM_PART_IS_DEFINED(SAM3S2A) || \ + SAM_PART_IS_DEFINED(SAM3S2B) || \ + SAM_PART_IS_DEFINED(SAM3S2C) \ + ) + +#define SAM3S4 ( \ + SAM_PART_IS_DEFINED(SAM3S4A) || \ + SAM_PART_IS_DEFINED(SAM3S4B) || \ + SAM_PART_IS_DEFINED(SAM3S4C) \ + ) + +#define SAM3S8 ( \ + SAM_PART_IS_DEFINED(SAM3S8B) || \ + SAM_PART_IS_DEFINED(SAM3S8C) \ + ) + +#define SAM3SD8 ( \ + SAM_PART_IS_DEFINED(SAM3SD8B) || \ + SAM_PART_IS_DEFINED(SAM3SD8C) \ + ) +/** @} */ + +/** + * \name SAM3U series + * @{ + */ +#define SAM3U1 ( \ + SAM_PART_IS_DEFINED(SAM3U1C) || \ + SAM_PART_IS_DEFINED(SAM3U1E) \ + ) + +#define SAM3U2 ( \ + SAM_PART_IS_DEFINED(SAM3U2C) || \ + SAM_PART_IS_DEFINED(SAM3U2E) \ + ) + +#define SAM3U4 ( \ + SAM_PART_IS_DEFINED(SAM3U4C) || \ + SAM_PART_IS_DEFINED(SAM3U4E) \ + ) +/** @} */ + +/** + * \name SAM3N series + * @{ + */ +#define SAM3N1 ( \ + SAM_PART_IS_DEFINED(SAM3N1A) || \ + SAM_PART_IS_DEFINED(SAM3N1B) || \ + SAM_PART_IS_DEFINED(SAM3N1C) \ + ) + +#define SAM3N2 ( \ + SAM_PART_IS_DEFINED(SAM3N2A) || \ + SAM_PART_IS_DEFINED(SAM3N2B) || \ + SAM_PART_IS_DEFINED(SAM3N2C) \ + ) + +#define SAM3N4 ( \ + SAM_PART_IS_DEFINED(SAM3N4A) || \ + SAM_PART_IS_DEFINED(SAM3N4B) || \ + SAM_PART_IS_DEFINED(SAM3N4C) \ + ) +/** @} */ + +/** + * \name SAM3X series + * @{ + */ +#define SAM3X4 ( \ + SAM_PART_IS_DEFINED(SAM3X4C) || \ + SAM_PART_IS_DEFINED(SAM3X4E) \ + ) + +#define SAM3X8 ( \ + SAM_PART_IS_DEFINED(SAM3X8C) || \ + SAM_PART_IS_DEFINED(SAM3X8E) || \ + SAM_PART_IS_DEFINED(SAM3X8H) \ + ) +/** @} */ + +/** + * \name SAM3A series + * @{ + */ +#define SAM3A4 ( \ + SAM_PART_IS_DEFINED(SAM3A4C) \ + ) + +#define SAM3A8 ( \ + SAM_PART_IS_DEFINED(SAM3A8C) \ + ) +/** @} */ + +/** + * \name SAM4S series + * @{ + */ +#define SAM4S8 ( \ + SAM_PART_IS_DEFINED(SAM4S8B) || \ + SAM_PART_IS_DEFINED(SAM4S8C) \ + ) + +#define SAM4S16 ( \ + SAM_PART_IS_DEFINED(SAM4S16B) || \ + SAM_PART_IS_DEFINED(SAM4S16C) \ + ) + +#define SAM4SA16 ( \ + SAM_PART_IS_DEFINED(SAM4SA16B) || \ + SAM_PART_IS_DEFINED(SAM4SA16C) \ + ) + +#define SAM4SD16 ( \ + SAM_PART_IS_DEFINED(SAM4SD16B) || \ + SAM_PART_IS_DEFINED(SAM4SD16C) \ + ) + +#define SAM4SD32 ( \ + SAM_PART_IS_DEFINED(SAM4SD32B) || \ + SAM_PART_IS_DEFINED(SAM4SD32C) \ + ) +/** @} */ + +/** + * \name SAM4L series + * @{ + */ +#define SAM4LS ( \ + SAM_PART_IS_DEFINED(SAM4LS2A) || \ + SAM_PART_IS_DEFINED(SAM4LS2B) || \ + SAM_PART_IS_DEFINED(SAM4LS2C) || \ + SAM_PART_IS_DEFINED(SAM4LS4A) || \ + SAM_PART_IS_DEFINED(SAM4LS4B) || \ + SAM_PART_IS_DEFINED(SAM4LS4C) \ + ) + +#define SAM4LC ( \ + SAM_PART_IS_DEFINED(SAM4LC2A) || \ + SAM_PART_IS_DEFINED(SAM4LC2B) || \ + SAM_PART_IS_DEFINED(SAM4LC2C) || \ + SAM_PART_IS_DEFINED(SAM4LC4A) || \ + SAM_PART_IS_DEFINED(SAM4LC4B) || \ + SAM_PART_IS_DEFINED(SAM4LC4C) \ + ) +/** @} */ + +/** + * \name SAM4E series + * @{ + */ +#define SAM4E8 ( \ + SAM_PART_IS_DEFINED(SAM4E8E) \ + ) + +#define SAM4E16 ( \ + SAM_PART_IS_DEFINED(SAM4E16E) \ + ) +/** @} */ + +/** + * \name SAM families + * @{ + */ +/** SAM3S Family */ +#define SAM3S (SAM3S1 || SAM3S2 || SAM3S4 || SAM3S8 || SAM3SD8) + +/** SAM3U Family */ +#define SAM3U (SAM3U1 || SAM3U2 || SAM3U4) + +/** SAM3N Family */ +#define SAM3N (SAM3N1 || SAM3N2 || SAM3N4) + +/** SAM3XA Family */ +#define SAM3XA (SAM3X4 || SAM3X8 || SAM3A4 || SAM3A8) + +/** SAM4S Family */ +#define SAM4S (SAM4S8 || SAM4S16 || SAM4SA16 || SAM4SD16 || SAM4SD32) + +/** SAM4L Family */ +#define SAM4L (SAM4LS || SAM4LC) + +/** SAM4E Family */ +#define SAM4E (SAM4E8 || SAM4E16) +/** @} */ + +/** SAM product line */ +#define SAM (SAM3S || SAM3U || SAM3N || SAM3XA || SAM4S || SAM4L || SAM4E) + +/** @} */ + +/** @} */ + +/** @} */ + +#endif /* ATMEL_PARTS_H */ diff --git a/bacnet-stack/ports/xplained/ASF/common/utils/stdio/read.c b/bacnet-stack/ports/xplained/ASF/common/utils/stdio/read.c new file mode 100644 index 00000000..d17afda2 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/utils/stdio/read.c @@ -0,0 +1,164 @@ +/** + * \file + * + * \brief System-specific implementation of the \ref _read function used by + * the standard library. + * + * Copyright (c) 2009-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#include "compiler.h" + +/** + * \defgroup group_common_utils_stdio Standard I/O (stdio) + * + * Common standard I/O driver that implements the stdio + * read and write functions on AVR and SAM devices. + * + * \{ + */ + +extern volatile void *volatile stdio_base; +void (*ptr_get)(void volatile*, char*); + + +// IAR common implementation +#if ( defined(__ICCAVR32__) || defined(__ICCAVR__) || defined(__ICCARM__) ) + +#include + +_STD_BEGIN + +#pragma module_name = "?__read" + +/*! \brief Reads a number of bytes, at most \a size, into the memory area + * pointed to by \a buffer. + * + * \param handle File handle to read from. + * \param buffer Pointer to buffer to write read bytes to. + * \param size Number of bytes to read. + * + * \return The number of bytes read, \c 0 at the end of the file, or + * \c _LLIO_ERROR on failure. + */ +size_t __read(int handle, unsigned char *buffer, size_t size) +{ + int nChars = 0; + // This implementation only reads from stdin. + // For all other file handles, it returns failure. + if (handle != _LLIO_STDIN) { + return _LLIO_ERROR; + } + for (; size > 0; --size) { + ptr_get(stdio_base, (char*)buffer); + buffer++; + nChars++; + } + return nChars; +} + +/*! \brief This routine is required by IAR DLIB library since EWAVR V6.10 + * the implementation is empty to be compatible with old IAR version. + */ +int __close(int handle) +{ + UNUSED(handle); + return 0; +} + +/*! \brief This routine is required by IAR DLIB library since EWAVR V6.10 + * the implementation is empty to be compatible with old IAR version. + */ +int remove(const char* val) +{ + UNUSED(val); + return 0; +} + +/*! \brief This routine is required by IAR DLIB library since EWAVR V6.10 + * the implementation is empty to be compatible with old IAR version. + */ +long __lseek(int handle, long val, int val2) +{ + UNUSED(handle); + UNUSED(val2); + return val; +} + +_STD_END + +// GCC AVR32 and SAM implementation +#elif (defined(__GNUC__) && !XMEGA && !MEGA) + +int __attribute__((weak)) +_read (int file, char * ptr, int len); // Remove GCC compiler warning + +int __attribute__((weak)) +_read (int file, char * ptr, int len) +{ + int nChars = 0; + + if (file != 0) { + return -1; + } + + for (; len > 0; --len) { + ptr_get(stdio_base, ptr); + ptr++; + nChars++; + } + return nChars; +} + +// GCC AVR implementation +#elif (defined(__GNUC__) && (XMEGA || MEGA) ) + +int _read (int *f); // Remove GCC compiler warning + +int _read (int *f) +{ + char c; + ptr_get(stdio_base,&c); + return c; +} +#endif + +/** + * \} + */ + diff --git a/bacnet-stack/ports/xplained/ASF/common/utils/stdio/stdio_serial/stdio_serial.h b/bacnet-stack/ports/xplained/ASF/common/utils/stdio/stdio_serial/stdio_serial.h new file mode 100644 index 00000000..600e664b --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/utils/stdio/stdio_serial/stdio_serial.h @@ -0,0 +1,123 @@ +/** + * + * \file + * + * \brief Common Standard I/O Serial Management. + * + * This file defines a useful set of functions for the Stdio Serial interface on AVR + * and SAM devices. + * + * Copyright (c) 2009-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + ******************************************************************************/ + + +#ifndef _STDIO_SERIAL_H_ +#define _STDIO_SERIAL_H_ + +/** + * \defgroup group_common_utils_stdio_stdio_serial Standard serial I/O (stdio) + * \ingroup group_common_utils_stdio + * + * Common standard serial I/O management driver that + * implements a stdio serial interface on AVR and SAM devices. + * + * \{ + */ + +#include +#include "compiler.h" +#include "sysclk.h" +#include "serial.h" + +#if (XMEGA || MEGA_RF) && defined(__GNUC__) + extern int _write (char c, int *f); + extern int _read (int *f); +#endif + + +//! Pointer to the base of the USART module instance to use for stdio. +extern volatile void *volatile stdio_base; +//! Pointer to the external low level write function. +extern int (*ptr_put)(void volatile*, char); +//! Pointer to the external low level read function. +extern void (*ptr_get)(void volatile*, char*); + +/*! \brief Initializes the stdio in Serial Mode. + * + * \param usart Base address of the USART instance. + * \param opt Options needed to set up RS232 communication (see \ref usart_options_t). + * + */ +static inline void stdio_serial_init(volatile void *usart, const usart_serial_options_t *opt) +{ + stdio_base = (void *)usart; + ptr_put = (int (*)(void volatile*,char))&usart_serial_putchar; + ptr_get = (void (*)(void volatile*,char*))&usart_serial_getchar; +#if (XMEGA || MEGA_RF) + usart_serial_init((USART_t *)usart,opt); +#elif UC3 + usart_serial_init(usart,(usart_serial_options_t *)opt); +#elif SAM + usart_serial_init((Usart *)usart,(usart_serial_options_t *)opt); +#else +# error Unsupported chip type +#endif + +#if defined(__GNUC__) +# if (XMEGA || MEGA_RF) + // For AVR GCC libc print redirection uses fdevopen. + fdevopen((int (*)(char, FILE*))(_write),(int (*)(FILE*))(_read)); +# endif +# if UC3 || SAM + // For AVR32 and SAM GCC + // Specify that stdout and stdin should not be buffered. + setbuf(stdout, NULL); + setbuf(stdin, NULL); + // Note: Already the case in IAR's Normal DLIB default configuration + // and AVR GCC library: + // - printf() emits one character at a time. + // - getchar() requests only 1 byte to exit. +# endif +#endif +} + +/** + * \} + */ + +#endif // _STDIO_SERIAL_H_ diff --git a/bacnet-stack/ports/xplained/ASF/common/utils/stdio/write.c b/bacnet-stack/ports/xplained/ASF/common/utils/stdio/write.c new file mode 100644 index 00000000..a661dece --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/common/utils/stdio/write.c @@ -0,0 +1,144 @@ +/** + * \file + * + * \brief System-specific implementation of the \ref _write function used by + * the standard library. + * + * Copyright (c) 2009-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#include "compiler.h" + +/** + * \addtogroup group_common_utils_stdio + * + * \{ + */ + +volatile void *volatile stdio_base; +int (*ptr_put)(void volatile*, char); + + +#if ( defined(__ICCAVR32__) || defined(__ICCAVR__) || defined(__ICCARM__)) + +#include + +_STD_BEGIN + +#pragma module_name = "?__write" + +/*! \brief Writes a number of bytes, at most \a size, from the memory area + * pointed to by \a buffer. + * + * If \a buffer is zero then \ref __write performs flushing of internal buffers, + * if any. In this case, \a handle can be \c -1 to indicate that all handles + * should be flushed. + * + * \param handle File handle to write to. + * \param buffer Pointer to buffer to read bytes to write from. + * \param size Number of bytes to write. + * + * \return The number of bytes written, or \c _LLIO_ERROR on failure. + */ +size_t __write(int handle, const unsigned char *buffer, size_t size) +{ + size_t nChars = 0; + + if (buffer == 0) { + // This means that we should flush internal buffers. + return 0; + } + + // This implementation only writes to stdout and stderr. + // For all other file handles, it returns failure. + if (handle != _LLIO_STDOUT && handle != _LLIO_STDERR) { + return _LLIO_ERROR; + } + + for (; size != 0; --size) { + if (ptr_put(stdio_base, *buffer++) < 0) { + return _LLIO_ERROR; + } + ++nChars; + } + return nChars; +} + +_STD_END + + +#elif (defined(__GNUC__) && !XMEGA && !MEGA) + +int __attribute__((weak)) +_write (int file, const char *ptr, int len); + +int __attribute__((weak)) +_write (int file, const char *ptr, int len) +{ + int nChars = 0; + + if ((file != 1) && (file != 2) && (file!=3)) { + return -1; + } + + for (; len != 0; --len) { + if (ptr_put(stdio_base, *ptr++) < 0) { + return -1; + } + ++nChars; + } + return nChars; +} + +#elif (defined(__GNUC__) && (XMEGA || MEGA)) + +int _write (char c, int *f); + +int _write (char c, int *f) +{ + if (ptr_put(stdio_base, c) < 0) { + return -1; + } + return 1; +} +#endif + +/** + * \} + */ + diff --git a/bacnet-stack/ports/xplained/ASF/xmega/boards/xmega_a3bu_xplained/init.c b/bacnet-stack/ports/xplained/ASF/xmega/boards/xmega_a3bu_xplained/init.c new file mode 100644 index 00000000..4cdf2605 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/boards/xmega_a3bu_xplained/init.c @@ -0,0 +1,164 @@ +/** + * \file + * + * \brief XMEGA-A3BU Xplained board init. + * + * This file contains board initialization function. + * + * Copyright (c) 2010 - 2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include +#include +#include + +/** + * \addtogroup xmega_a3bu_xplained_group + * @{ + */ + +void board_init(void) +{ + #if 0 + ioport_configure_pin(LED0_GPIO, IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(LED1_GPIO, IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(LED2_GPIO, IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(LED3_GPIO, + IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW | IOPORT_INV_ENABLED); + + ioport_configure_pin(GPIO_PUSH_BUTTON_0, + IOPORT_DIR_INPUT | IOPORT_LEVEL | IOPORT_PULL_UP); + ioport_configure_pin(GPIO_PUSH_BUTTON_1, + IOPORT_DIR_INPUT | IOPORT_LEVEL | IOPORT_PULL_UP); + ioport_configure_pin(GPIO_PUSH_BUTTON_2, + IOPORT_DIR_INPUT | IOPORT_LEVEL | IOPORT_PULL_UP); + +#ifdef CONF_BOARD_C12832A1Z + ioport_configure_pin(NHD_C12832A1Z_SPI_SCK, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(NHD_C12832A1Z_SPI_MOSI, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(NHD_C12832A1Z_CSN, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(NHD_C12832A1Z_REGISTER_SELECT, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(NHD_C12832A1Z_RESETN, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(NHD_C12832A1Z_BACKLIGHT, + IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW); +#endif + +#ifdef CONF_BOARD_AT45DBX + ioport_configure_pin(AT45DBX_MASTER_SCK, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(AT45DBX_MASTER_MOSI, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(AT45DBX_MASTER_MISO, IOPORT_DIR_INPUT); + ioport_configure_pin(AT45DBX_CS, IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); +#endif + +#ifdef CONF_BOARD_ENABLE_MXT143E_XPLAINED + ioport_configure_pin(MXT143E_XPLAINED_SCK, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(MXT143E_XPLAINED_MOSI, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(MXT143E_XPLAINED_MISO, IOPORT_DIR_INPUT); + ioport_configure_pin(MXT143E_XPLAINED_CS, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(MXT143E_XPLAINED_CHG, IOPORT_DIR_INPUT); + ioport_configure_pin(MXT143E_XPLAINED_DC, + IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW); +#ifndef MXT143E_XPLAINED_BACKLIGHT_DISABLE + ioport_configure_pin(MXT143E_XPLAINED_BACKLIGHT, + IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW); +#endif + ioport_configure_pin(MXT143E_XPLAINED_LCD_RESET, + IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW); +#endif + +#ifdef CONF_BOARD_ENABLE_AC_PINS + ioport_configure_pin(IOPORT_CREATE_PIN(PORTA, 0), IOPORT_DIR_INPUT); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTA, 2), IOPORT_DIR_INPUT); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTB, 1), IOPORT_DIR_INPUT); +#endif + +#ifdef CONF_BOARD_ENABLE_USARTC0 + ioport_configure_pin(IOPORT_CREATE_PIN(PORTC, 3), + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTC, 2), IOPORT_DIR_INPUT); +#endif + +#ifdef CONF_BOARD_ENABLE_USARTD0 + ioport_configure_pin(IOPORT_CREATE_PIN(PORTD, 3), + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTD, 2), IOPORT_DIR_INPUT); +#endif + +#ifdef CONF_BOARD_ENABLE_USARTE0 + ioport_configure_pin(IOPORT_CREATE_PIN(PORTE, 3), + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTE, 2), IOPORT_DIR_INPUT); +#endif + +#if defined (SENSORS_XPLAINED_BOARD) + /* Configure the Xplained Sensor extension board, if any, after + * the platform Xplained board has configured basic clock settings, + * GPIO pin mapping, interrupt controller options, etc. + */ + sensor_board_init(); +#endif + +#ifdef CONF_BOARD_AT86RFX + ioport_configure_pin(AT86RFX_SPI_SCK, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(AT86RFX_SPI_MOSI, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(AT86RFX_SPI_MISO, IOPORT_DIR_INPUT); + ioport_configure_pin(AT86RFX_SPI_CS, IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + + /* Initialize TRX_RST and SLP_TR as GPIO. */ + ioport_configure_pin(AT86RFX_RST_PIN, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + ioport_configure_pin(AT86RFX_SLP_PIN, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); +#endif +#endif +} + +/** + * @} + */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/boards/xmega_a3bu_xplained/led.h b/bacnet-stack/ports/xplained/ASF/xmega/boards/xmega_a3bu_xplained/led.h new file mode 100644 index 00000000..46131cac --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/boards/xmega_a3bu_xplained/led.h @@ -0,0 +1,80 @@ +/** + * \file + * + * \brief XMEGA-A3BU board LEDs support package. + * + * This file contains definitions and services related to the LED features of + * the XMEGA-A3BU Xplained board. + * + * To use this board, define BOARD=XMEGA_A3BU_XPLAINED. + * + * Copyright (c) 2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _LED_H_ +#define _LED_H_ + +#include "gpio.h" + +/** + * \brief Turns off the specified LEDs. + * + * \param led_gpio LED to turn off (LEDx_GPIO). + * + * \note The pins of the specified LEDs are set to GPIO output mode. + */ +#define LED_Off(led_gpio) gpio_set_pin_high(led_gpio) + +/** + * \brief Turns on the specified LEDs. + * + * \param led_gpio LED to turn on (LEDx_GPIO). + * + * \note The pins of the specified LEDs are set to GPIO output mode. + */ +#define LED_On(led_gpio) gpio_set_pin_low(led_gpio) + +/** + * \brief Toggles the specified LEDs. + * + * \param led_gpio LED to toggle (LEDx_GPIO). + * + * \note The pins of the specified LEDs are set to GPIO output mode. + */ +#define LED_Toggle(led_gpio) gpio_toggle_pin(led_gpio) + +#endif /* _LED_H_ */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/boards/xmega_a3bu_xplained/xmega_a3bu_xplained.h b/bacnet-stack/ports/xplained/ASF/xmega/boards/xmega_a3bu_xplained/xmega_a3bu_xplained.h new file mode 100644 index 00000000..c4c76e47 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/boards/xmega_a3bu_xplained/xmega_a3bu_xplained.h @@ -0,0 +1,414 @@ +/** + * \file + * + * \brief XMEGA-A3BU-XPLAINED board header file. + * + * This file contains definitions and services related to the features of the + * XMEGA-A3BU Xplained board. + * + * To use this board define BOARD=XMEGA_A3BU_XPLAINED. + * + * Copyright (c) 2011 - 2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _XMEGA_A3BU_XPLAINED_H_ +#define _XMEGA_A3BU_XPLAINED_H_ + +#include + +#define MCU_SOC_NAME "ATxmega256A3BU" +#define BOARD_NAME "XMEGA-A3BU-XPLAINED" +/** + * \defgroup xmega_a3bu_xplained_group XMEGA-A3BU Xplained board + * @{ + */ + +/** + * \defgroup xmega_a3bu_xplained_feature_group Feature definitions + * @{ + */ + +//! \name Miscellaneous data +//@{ +//! Validate board support for the common sensor service. +#define COMMON_SENSOR_PLATFORM +//@} + +/** + * \name LEDs + * + * LED0 and LED1 are single yellow LEDs that are active low.. + * LED2 and LED3 are inside one package (Led red and green close + * to USB connector) but can be controlled individually. + * LED2 has a red color and is active low. This LED can be + * used for general purposes. + * LED3 has a green color and is active high. By default this + * LED is on since it shall indicate that power is applied to the + * board. By pulling the gate of a N-FET low it is possible to + * turn off the LED if needed. + */ +//@{ +#define LED0_GPIO IOPORT_CREATE_PIN(PORTR, 0) +#define LED1_GPIO IOPORT_CREATE_PIN(PORTR, 1) +#define LED2_GPIO IOPORT_CREATE_PIN(PORTD, 4) +#define LED3_GPIO IOPORT_CREATE_PIN(PORTD, 5) +#define LED0 LED0_GPIO +#define LED1 LED1_GPIO +#define LED2 LED2_GPIO +#define LED3 LED3_GPIO +//! Number of LEDs. +#define LED_COUNT 4 +//@} + +//! \name Push buttons +//@{ +#define GPIO_PUSH_BUTTON_0 IOPORT_CREATE_PIN(PORTE, 5) +#define GPIO_PUSH_BUTTON_1 IOPORT_CREATE_PIN(PORTF, 1) +#define GPIO_PUSH_BUTTON_2 IOPORT_CREATE_PIN(PORTF, 2) +//@} + +//! \name QTouch button +//! This button requires the software QTouch library +//@{ +#define QTOUCH_BUTTON_SNS IOPORT_CREATE_PIN(PORTF, 6) +#define QTOUCH_BUTTON_SNSK IOPORT_CREATE_PIN(PORTF, 7) +//@} + +//! \name Light sensor +//@{ +#define LIGHT_SENSOR_ADC_MODULE ADCA +#define LIGHT_SENSOR_ADC_INPUT ADCCH_POS_PIN0 +#define LIGHT_SENSOR_SIGNAL_PIN IOPORT_CREATE_PIN(PORTA, 0) +//@} + +//! \name Temperature sensor (NTC) +//@{ +#define TEMPERATURE_SENSOR_ADC_MODULE ADCA +#define TEMPERATURE_SENSOR_ADC_INPUT ADCCH_POS_PIN1 +#define TEMPERATURE_SENSOR_SIGNAL_PIN IOPORT_CREATE_PIN(PORTA, 1) +//@} + +//! \name Analog filter (lowpass RC @ 159 Hz) +//@{ +#define FILTER_INPUT_SIGNAL_PIN IOPORT_CREATE_PIN(PORTF, 0) +#define FILTER_OUTPUT_ADC_MODULE ADCA +#define FILTER_OUTPUT_ADC_INPUT ADCCH_POS_PIN2 +#define FILTER_OUTPUT_SIGNAL_PIN IOPORT_CREATE_PIN(PORTA, 2) +//@} + +//! \name LCD backlight +//@{ +#define LCD_BACKLIGHT_ENABLE_PIN IOPORT_CREATE_PIN(PORTE, 4) +#define LCD_BACKLIGHT_ENABLE_LEVEL true +//@} + +//! \name LCD controller (NHD-C12832A1Z-FSW-FBW-3V3) +//@{ +#define NHD_C12832A1Z_SPI &USARTD0 +#define NHD_C12832A1Z_SPI_SCK IOPORT_CREATE_PIN(PORTD, 1) +#define NHD_C12832A1Z_SPI_MOSI IOPORT_CREATE_PIN(PORTD, 3) +#define NHD_C12832A1Z_CSN IOPORT_CREATE_PIN(PORTF, 3) +//! If this signal is set high display data is sent otherwise commands +#define NHD_C12832A1Z_REGISTER_SELECT IOPORT_CREATE_PIN(PORTD, 0) +#define NHD_C12832A1Z_RESETN IOPORT_CREATE_PIN(PORTA, 3) +//! The backlight is active high +#define NHD_C12832A1Z_BACKLIGHT IOPORT_CREATE_PIN(PORTE, 4) +//@} + +//! \name LCD dimensions +//@{ +#define LCD_WIDTH_PIXELS (128) +#define LCD_HEIGHT_PIXELS (32) +//@} + +//! \name DataFlash memory (AT45DBX) +//@{ +#define AT45DBX_SPI &USARTD0 +#define AT45DBX_CS IOPORT_CREATE_PIN(PORTF, 4) +//! SCK pin +#define AT45DBX_MASTER_SCK IOPORT_CREATE_PIN(PORTD, 1) +//! MOSI pin +#define AT45DBX_MASTER_MOSI IOPORT_CREATE_PIN(PORTD, 3) +//! MISO pin +#define AT45DBX_MASTER_MISO IOPORT_CREATE_PIN(PORTD, 2) +//@} + +//! \name MXT143E Xplained top module +//@{ +#define MXT143E_XPLAINED_TWI &TWIC +#define MXT143E_XPLAINED_USART_SPI &USARTC1 +#define MXT143E_XPLAINED_CS IOPORT_CREATE_PIN(PORTC, 4) +#define MXT143E_XPLAINED_SCK IOPORT_CREATE_PIN(PORTC, 7) +#define MXT143E_XPLAINED_MOSI IOPORT_CREATE_PIN(PORTC, 5) +#define MXT143E_XPLAINED_MISO IOPORT_CREATE_PIN(PORTC, 6) +#define MXT143E_XPLAINED_CHG IOPORT_CREATE_PIN(PORTC, 2) +#define MXT143E_XPLAINED_DC IOPORT_CREATE_PIN(PORTC, 3) +#define MXT143E_XPLAINED_BACKLIGHT IOPORT_CREATE_PIN(PORTA, 4) +#define MXT143E_XPLAINED_LCD_RESET IOPORT_CREATE_PIN(PORTA, 6) +//@} + +/** + * \name External oscillator + * + * \todo Need to measure the actual startup time on the hardware. + */ +//@{ +#define BOARD_XOSC_HZ 32768 +#define BOARD_XOSC_TYPE XOSC_TYPE_32KHZ +#define BOARD_XOSC_STARTUP_US 1000000 +//@} + +//! \name Communication interfaces on header J1 +//@{ +#define TWIC_SDA IOPORT_CREATE_PIN(PORTC, 0) +#define TWIC_SCL IOPORT_CREATE_PIN(PORTC, 1) +#define USARTC0_RXD IOPORT_CREATE_PIN(PORTC, 2) +#define USARTC0_TXD IOPORT_CREATE_PIN(PORTC, 3) +#define SPIC_SS IOPORT_CREATE_PIN(PORTC, 4) +#define SPIC_MOSI IOPORT_CREATE_PIN(PORTC, 5) +#define SPIC_MISO IOPORT_CREATE_PIN(PORTC, 6) +#define SPIC_SCK IOPORT_CREATE_PIN(PORTC, 7) +//@} + + +/*! \name Connections of the AT86RFX transceiver + */ +//! @{ +#define AT86RFX_SPI &SPIC +#define AT86RFX_RST_PIN IOPORT_CREATE_PIN(PORTC, 0) +#define AT86RFX_MISC_PIN IOPORT_CREATE_PIN(PORTC, 1) +#define AT86RFX_IRQ_PIN IOPORT_CREATE_PIN(PORTC, 2) +#define AT86RFX_SLP_PIN IOPORT_CREATE_PIN(PORTC, 3) +#define AT86RFX_SPI_CS IOPORT_CREATE_PIN(PORTC, 4) +#define AT86RFX_SPI_MOSI IOPORT_CREATE_PIN(PORTC, 5) +#define AT86RFX_SPI_MISO IOPORT_CREATE_PIN(PORTC, 6) +#define AT86RFX_SPI_SCK IOPORT_CREATE_PIN(PORTC, 7) + +#define AT86RFX_INTC_INIT() ioport_configure_pin(AT86RFX_IRQ_PIN, IOPORT_DIR_INPUT); \ + PORTC.PIN2CTRL = PORT_ISC0_bm; \ + PORTC.INT0MASK = PIN2_bm; \ + PORTC.INTFLAGS = PORT_INT0IF_bm; + +#define AT86RFX_ISR() ISR(PORTC_INT0_vect) + +/** Enables the transceiver main interrupt. */ +#define ENABLE_TRX_IRQ() (PORTC.INTCTRL |= PORT_INT0LVL_gm) + +/** Disables the transceiver main interrupt. */ +#define DISABLE_TRX_IRQ() (PORTC.INTCTRL &= ~PORT_INT0LVL_gm) + +/** Clears the transceiver main interrupt. */ +#define CLEAR_TRX_IRQ() (PORTC.INTFLAGS = PORT_INT0IF_bm) + +/* + * This macro saves the trx interrupt status and disables the trx interrupt. + */ +#define ENTER_TRX_REGION() { uint8_t irq_mask = PORTC.INTCTRL; PORTC.INTCTRL &= ~PORT_INT0LVL_gm + +/* + * This macro restores the transceiver interrupt status + */ +#define LEAVE_TRX_REGION() PORTC.INTCTRL = irq_mask; } + +//! @} + + +/** + * \name Pin connections on header J1 + * + * The whole port C is directly connected to J1. + * + * \note For the TWI lines there are footprints for pull up resistors, which + * are by default not mounted on the board. + */ +//@{ +#define J1_PIN0 IOPORT_CREATE_PIN(PORTC, 0) +#define J1_PIN1 IOPORT_CREATE_PIN(PORTC, 1) +#define J1_PIN2 IOPORT_CREATE_PIN(PORTC, 2) +#define J1_PIN3 IOPORT_CREATE_PIN(PORTC, 3) +#define J1_PIN4 IOPORT_CREATE_PIN(PORTC, 4) +#define J1_PIN5 IOPORT_CREATE_PIN(PORTC, 5) +#define J1_PIN6 IOPORT_CREATE_PIN(PORTC, 6) +#define J1_PIN7 IOPORT_CREATE_PIN(PORTC, 7) +//@} + +/** + * \name Pin connections on header J2 + * + * The lower half of port B is connected to the lower pins of J2 while the + * upper half of port A is connected to the upper pins of J2. + * + * The port pins are connected directly to this header and are not shared with + * any on-board functionality. + */ +//@{ +#define J2_PIN0 IOPORT_CREATE_PIN(PORTB, 0) +#define J2_PIN1 IOPORT_CREATE_PIN(PORTB, 1) +#define J2_PIN2 IOPORT_CREATE_PIN(PORTB, 2) +#define J2_PIN3 IOPORT_CREATE_PIN(PORTB, 3) +#define J2_PIN4 IOPORT_CREATE_PIN(PORTA, 4) +#define J2_PIN5 IOPORT_CREATE_PIN(PORTA, 5) +#define J2_PIN6 IOPORT_CREATE_PIN(PORTA, 6) +#define J2_PIN7 IOPORT_CREATE_PIN(PORTA, 7) +//@} + +/** + * \name Pin connections on header J3 + * + * The lower half of port A is connected to the lower pins of J3 while the + * upper half of port B is connected to the upper pins of J3. + * + * Following pins are shared with on-board functionality: + * - J3_PIN0 Light sensor output (can be disconnected via scratch pad) + * - J3_PIN1 NTC sensor output (can be disconnected via scratch pad) + * - J3_PIN2 Filter output (can be disconnected via scratch pad) + * - J3_PIN3 Display reset input (can't be used when display is in use) + * - J3_PIN4 JTAG TMS (pin can't be used while JTAG device connected) + * - J3_PIN5 JTAG TDI (pin can't be used while JTAG device connected) + * - J3_PIN6 JTAG TCK (pin can't be used while JTAG device connected) + * - J3_PIN7 JTAG TDO & PID DATA (pin can't be used while JTAG/PDI device is + * connected) + */ +//@{ +#define J3_PIN0 IOPORT_CREATE_PIN(PORTA, 0) +#define J3_PIN1 IOPORT_CREATE_PIN(PORTA, 1) +#define J3_PIN2 IOPORT_CREATE_PIN(PORTA, 2) +#define J3_PIN3 IOPORT_CREATE_PIN(PORTA, 3) +#define J3_PIN4 IOPORT_CREATE_PIN(PORTB, 4) +#define J3_PIN5 IOPORT_CREATE_PIN(PORTB, 5) +#define J3_PIN6 IOPORT_CREATE_PIN(PORTB, 6) +#define J3_PIN7 IOPORT_CREATE_PIN(PORTB, 7) +//@} + +/** + * \name Pin connections on header J4 + * + * The lower half of port E is connected to the lower pins of J4 and the lower + * half of port D is connected to the upper pins of J4. + */ +//@{ +#define J4_PIN0 IOPORT_CREATE_PIN(PORTE, 0) +#define J4_PIN1 IOPORT_CREATE_PIN(PORTE, 1) +#define J4_PIN2 IOPORT_CREATE_PIN(PORTE, 2) +#define J4_PIN3 IOPORT_CREATE_PIN(PORTE, 3) +#define J4_PIN4 IOPORT_CREATE_PIN(PORTD, 0) +#define J4_PIN5 IOPORT_CREATE_PIN(PORTD, 3) +#define J4_PIN6 IOPORT_CREATE_PIN(PORTD, 2) +#define J4_PIN7 IOPORT_CREATE_PIN(PORTD, 1) +//@} + +/** + * @} + */ + +/** + * \defgroup xmega_a3bu_xplained_config_group Configuration options + * @{ + */ + +#if defined(__DOXYGEN__) + +/** + * \name Initialization + * \note Define these symbols in \ref conf_board.h to enable the corresponding + * features. + */ +//@{ + +/** + * \def CONF_BOARD_C12832A1Z + * \brief Initialize SPI and control pins for C12832A1Z LCD controller + */ +# if !defined(CONF_BOARD_C12832A1Z) +# define CONF_BOARD_C12832A1Z +# endif + +/** + * \def CONF_BOARD_AT45DBX + * \brief Initialize SPI pins for AT45DBX DataFlash + */ +# if !defined(CONF_BOARD_AT45DBX) +# define CONF_BOARD_AT45DBX +# endif + +/** + * \def CONF_BOARD_ENABLE_AC_PINS + * \brief Initialize IO pins for Analog Comparator + * + * \note This initializes pins PA0, PA2 and PB1 as inputs. + */ +# if !defined(CONF_BOARD_ENABLE_AC_PINS) +# define CONF_BOARD_ENABLE_AC_PINS +# endif + +/** + * \def CONF_BOARD_ENABLE_USARTC0 + * \brief Initialize IO pins for USART 0 on port C + */ +# if !defined(CONF_BOARD_ENABLE_USARTC0) +# define CONF_BOARD_ENABLE_USARTC0 +# endif + +/** + * \def CONF_BOARD_ENABLE_USARTD0 + * \brief Initialize IO pins for USART 0 on port D + */ +# if !defined(CONF_BOARD_ENABLE_USARTD0) +# define CONF_BOARD_ENABLE_USARTD0 +# endif + +/** + * \def CONF_BOARD_ENABLE_USARTE0 + * \brief Initialize IO pins for USART 0 on port E + */ +# if !defined(CONF_BOARD_ENABLE_USARTE0) +# define CONF_BOARD_ENABLE_USARTE0 +# endif + +//@} + +#endif // __DOXYGEN__ + +/** + * @} + */ + +/** + * @} + */ + +#endif /* _XMEGA_A3BU_XPLAINED_H_ */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/adc/adc.c b/bacnet-stack/ports/xplained/ASF/xmega/drivers/adc/adc.c new file mode 100644 index 00000000..eb9b2fdd --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/adc/adc.c @@ -0,0 +1,297 @@ +/** + * \file + * + * \brief AVR XMEGA Analog to Digital Converter driver + * + * Copyright (C) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#include +#include + +/** + * \ingroup adc_module_group + * @{ + */ + +/** \name ADC interrupt callback function */ +/** @{ */ +#ifdef ADCA + +/** + * \internal + * \brief ADC A enable counter + * + * This is used to ensure that ADC A is not inadvertently disabled when its + * module or channel configurations are updated. + */ +static uint8_t adca_enable_count; + +# ifdef CONFIG_ADC_CALLBACK_ENABLE + +/** + * \internal + * \brief ADC A interrupt callback function pointer + */ +adc_callback_t adca_callback; + +# endif +#endif + +#ifdef ADCB + +/** + * \internal + * \brief ADC B enable counter + * + * This is used to ensure that ADC B is not inadvertently disabled when its + * module or channel configurations are updated. + */ +static uint8_t adcb_enable_count; + +# ifdef CONFIG_ADC_CALLBACK_ENABLE + +/** + * \internal + * \brief ADC B interrupt callback function pointer + */ +adc_callback_t adcb_callback; + +# endif +#endif + +#if defined(CONFIG_ADC_CALLBACK_ENABLE) || defined(__DOXYGEN__) + +/** + * \brief Set ADC interrupt callback function + * + * Sets a new callback function for interrupts on the specified ADC. + * + * \param adc Pointer to ADC module. + * \param callback Pointer to the callback function to set. + */ +void adc_set_callback(ADC_t * adc, + adc_callback_t callback) +{ + irqflags_t flags; + + Assert(callback); + + flags = cpu_irq_save(); + +#ifdef ADCA + if ((uintptr_t) adc == (uintptr_t) & ADCA) { + adca_callback = callback; + } else +#endif + +#ifdef ADCB + if ((uintptr_t) adc == (uintptr_t) & ADCB) { + adcb_callback = callback; + } else +#endif + + { + Assert(0); + } + + cpu_irq_restore(flags); +} + +#endif /* CONFIG_ADC_CALLBACK_ENABLE */ + +/** @} */ + +/** \name Internal functions for driver */ +/** @{ */ + +/** + * \internal + * \brief Enable peripheral clock for ADC + * + * Checks if the enable count for the ADC is zero, then increments it. If the + * count was zero, the peripheral clock is enabled. Otherwise, it is already + * enabled. + * + * \param adc Pointer to ADC module. + */ +void adc_enable_clock(ADC_t * adc); + +void adc_enable_clock(ADC_t * adc) +{ +#ifdef ADCA + if ((uintptr_t) adc == (uintptr_t) (&ADCA)) { + Assert(adca_enable_count < 0xff); + if (!adca_enable_count++) { + sysclk_enable_module(SYSCLK_PORT_A, SYSCLK_ADC); + } + } else +#endif + +#ifdef ADCB + if ((uintptr_t) adc == (uintptr_t) (&ADCB)) { + Assert(adcb_enable_count < 0xff); + if (!adcb_enable_count++) { + sysclk_enable_module(SYSCLK_PORT_B, SYSCLK_ADC); + } + } else +#endif + + { + Assert(0); + } +} + +/** + * \internal + * \brief Disable peripheral clock for ADC + * + * Decrements the enable count for the ADC, then disables its peripheral clock + * if the count hit zero. If the count did not hit zero, it indicates the ADC is + * enabled. + * + * \param adc Pointer to ADC module + */ +void adc_disable_clock(ADC_t * adc); + +void adc_disable_clock(ADC_t * adc) +{ +#ifdef ADCA + if ((uintptr_t) adc == (uintptr_t) (&ADCA)) { + Assert(adca_enable_count); + if (!--adca_enable_count) { + sysclk_disable_module(SYSCLK_PORT_A, SYSCLK_ADC); + } + } else +#endif + +#ifdef ADCB + if ((uintptr_t) adc == (uintptr_t) (&ADCB)) { + Assert(adcb_enable_count); + if (!--adcb_enable_count) { + sysclk_disable_module(SYSCLK_PORT_B, SYSCLK_ADC); + } + } else +#endif + + { + Assert(0); + } +} + +/** @} */ + +/** \name ADC module management */ +/** @{ */ + +/** + * \brief Enable ADC + * + * Enables the ADC and locks IDLE mode for the sleep manager. + * + * \param adc Pointer to ADC module + * + * \note To ensure accurate conversions, please wait for at least + * the specified start-up time between enabling the ADC module, and starting + * a conversion. For most XMEGA devices the start-up time is specified + * to be a maximum of 24 ADC clock cycles. Please verify the start-up time for + * the device in use. + */ +void adc_enable(ADC_t * adc) +{ + irqflags_t flags = cpu_irq_save(); + adc_enable_clock(adc); + adc->CTRLA |= ADC_ENABLE_bm; + cpu_irq_restore(flags); + + sleepmgr_lock_mode(SLEEPMGR_IDLE); +} + +/** + * \brief Disable ADC + * + * Disables the ADC and unlocks IDLE mode for the sleep manager. + * + * \param adc Pointer to ADC module + */ +void adc_disable(ADC_t * adc) +{ + irqflags_t flags = cpu_irq_save(); + adc->CTRLA &= ~ADC_ENABLE_bm; + adc_disable_clock(adc); + cpu_irq_restore(flags); + + sleepmgr_unlock_mode(SLEEPMGR_IDLE); +} + +/** + * \brief Check if the ADC is enabled + * + * \param adc Pointer to ADC module. + * + * \retval true if ADC is enabled. + * \retval false if ADC is disabled. + */ +bool adc_is_enabled(ADC_t * adc) +{ + /* It is sufficient to return the state of the ADC enable counters + * since all driver functions that change the counts are protected + * against interrupts and only the enable/disable functions leave the + * counts incremented/decremented upon return. + */ +#ifdef ADCA + if ((uintptr_t) adc == (uintptr_t) & ADCA) { + return adca_enable_count; + } else +#endif + +#ifdef ADCB + if ((uintptr_t) adc == (uintptr_t) & ADCB) { + return adcb_enable_count; + } else +#endif + + { + Assert(0); + return false; + } +} + +/** @} */ + +/** @} */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/adc/adc.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/adc/adc.h new file mode 100644 index 00000000..f376d1fa --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/adc/adc.h @@ -0,0 +1,2375 @@ +/** + * \file + * + * \brief AVR XMEGA Analog to Digital Converter driver + * + * Copyright (C) 2010-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef ADC_H +#define ADC_H + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Fix header error in ADC_CH_t structure about missing SCAN register */ +#ifndef ADC_CH_OFFSET_gp +# define ADC_CH_OFFSET_gp 4 /* Positive MUX setting offset group position. */ +# if XMEGA_A || XMEGA_D +# ifdef __ICCAVR__ +# define SCAN reserved_0x06 +# else +# define SCAN reserved_0x6 +# endif +# endif +#endif + +/* Fix header error */ +#define ADC_EVACT_SYNCSWEEP_gc (0x06 << 0) +#define ADC_REFSEL_INTVCC_gc (0x01 << 4) +#define ADC_REFSEL_VCCDIV2_gc (0x04 << 4) +#define ADC_CH_GAIN_DIV2_gc (0x07 << 2) +#if (!XMEGA_A) +/* ADC.CTRLB bit masks and bit positions */ +# define ADC_CURRLIMIT_NO_gc (0x00 << 5) +# define ADC_CURRLIMIT_LOW_gc (0x01 << 5) +# define ADC_CURRLIMIT_MED_gc (0x02 << 5) +# define ADC_CURRLIMIT_HIGH_gc (0x03 << 5) +#endif +#if (!XMEGA_A) && (!defined ADC_CURRLIMIT_gm) +/* ADC.CTRLB bit masks and bit positions */ +# define ADC_CURRLIMIT_gm 0x60 /* Current limit group mask. */ +#endif +#if (!XMEGA_E) +/* Negative input multiplexer selection without gain */ + typedef enum ADC_CH_MUXNEG_MODE10_enum + { + ADC_CH_MUXNEG_MODE10_PIN0_gc = (0x00 << 0), /* Input pin 0 */ + ADC_CH_MUXNEG_MODE10_PIN1_gc = (0x01 << 0), /* Input pin 1 */ + ADC_CH_MUXNEG_MODE10_PIN2_gc = (0x02 << 0), /* Input pin 2 */ + ADC_CH_MUXNEG_MODE10_PIN3_gc = (0x03 << 0), /* Input pin 3 */ + ADC_CH_MUXNEG_MODE10_GND_gc = (0x05 << 0), /* PAD ground */ + ADC_CH_MUXNEG_MODE10_INTGND_gc = (0x07 << 0), /* Internal ground */ + } + ADC_CH_MUXNEGL_t; + +/* Negative input multiplexer selection with gain */ + typedef enum ADC_CH_MUXNEG_MODE11_enum + { + ADC_CH_MUXNEG_MODE11_PIN4_gc = (0x00 << 0), /* Input pin 4 */ + ADC_CH_MUXNEG_MODE11_PIN5_gc = (0x01 << 0), /* Input pin 5 */ + ADC_CH_MUXNEG_MODE11_PIN6_gc = (0x02 << 0), /* Input pin 6 */ + ADC_CH_MUXNEG_MODE11_PIN7_gc = (0x03 << 0), /* Input pin 7 */ + ADC_CH_MUXNEG_MODE11_INTGND_gc = (0x04 << 0), /* Internal ground */ + ADC_CH_MUXNEG_MODE11_GND_gc = (0x05 << 0), /* PAD ground */ + } + ADC_CH_MUXNEGH_t; +#endif + +/** + * \defgroup adc_group Analog to Digital Converter (ADC) + * + * See \ref adc_quickstart. + * + * This is a driver for the AVR XMEGA ADC. It provides functions for enabling, + * disabling and configuring the ADC modules and their individual channels. + * + * The driver API is split in two parts: + * - \ref adc_channel_group + * - \ref adc_module_group + * + * Both APIs use structures that contain the configuration. These structures + * must be set up before the configuration is written to either an ADC module or + * one of their channels. + * + * After the ADC has been configured it must be enabled before any conversions + * may be performed. To ensure accurate conversions, please wait for at least + * the specified start-up time between enabling the ADC module, and starting + * a conversion. For most XMEGA devices the start-up time is specified + * to be a maximum of 24 ADC clock cycles. Please verify the start-up time for + * the device in use. + * + * \note Not all of the documented functions are available on all devices. This + * is due to differences in the ADC feature set. Refer to the device manual and + * datasheet for details on which features are available for a specific device. + * + * \note The functions for creating/changing configurations are not protected + * against interrupts. The functions that read from or write to the ADC's + * registers are protected unless otherwise noted. + * + * \section dependencies Dependencies + * This driver depends on the following modules: + * - \ref sysclk_group for peripheral clock control. + * - \ref sleepmgr_group for setting allowed sleep mode. + * - \ref nvm_group for getting factory calibration data. + * - \ref interrupt_group for ISR definition and disabling interrupts during + * critical code sections. + * @{ + */ + +/** + * \defgroup adc_module_group ADC module + * + * Management and configuration functions for the ADC module. + * + * The API functions and definitions can be divided in three groups: + * - interrupt callback: configure and set interrupt callback function. + * - module management: direct access for enabling and disabling the ADC, + * starting conversions, getting interrupt flags, etc. + * - module configuration: create/change configurations and write/read them + * to/from an ADC. + * + * @{ + */ + +/** + * \def ADC_NR_OF_CHANNELS + * \brief Number of channels per ADC + */ +#if XMEGA_A || XMEGA_AU || defined(__DOXYGEN__) +# define ADC_NR_OF_CHANNELS 4 +#elif XMEGA_B || XMEGA_C || XMEGA_D || XMEGA_E +# define ADC_NR_OF_CHANNELS 1 +#endif + +/** ADC configuration */ + struct adc_config + { +#if ADC_NR_OF_CHANNELS > 1 + /* DMA group request is stored in CTRLA */ + uint8_t ctrla; +#endif + uint8_t ctrlb; + uint8_t refctrl; + uint8_t evctrl; + uint8_t prescaler; + uint16_t cmp; +#if XMEGA_E + /* XMEGA E sample time value stored in SAMPCTRL */ + uint8_t sampctrl; +#endif + }; + +/** + * \name Calibration data addresses + * \note The temperature sensor calibration is sampled at 85 degrees Celsius + * with unsigned, 12-bit conversion. + */ +/** @{ */ + +/** ADC A, calibration byte 0. */ +#define ADCACAL0 offsetof(NVM_PROD_SIGNATURES_t, ADCACAL0) +/** ADC A, calibration byte 1. */ +#define ADCACAL1 offsetof(NVM_PROD_SIGNATURES_t, ADCACAL1) +/** ADC B, calibration byte 0. */ +#define ADCBCAL0 offsetof(NVM_PROD_SIGNATURES_t, ADCBCAL0) +/** ADC B, calibration byte 1. */ +#define ADCBCAL1 offsetof(NVM_PROD_SIGNATURES_t, ADCBCAL1) +/** Temperature sensor calibration byte 0. */ +#define TEMPSENSE0 offsetof(NVM_PROD_SIGNATURES_t, TEMPSENSE0) +/** Temperature sensor calibration byte 1. */ +#define TEMPSENSE1 offsetof(NVM_PROD_SIGNATURES_t, TEMPSENSE1) + +/** @} */ + +/** \brief ADC calibration data */ + enum adc_calibration_data + { + ADC_CAL_ADCA, /**< ADC A pipeline calibration data. */ + ADC_CAL_ADCB, /**< ADC B pipeline calibration data. */ + + /** + * \brief Temperature sensor calibration data. + * \note 12-bit unsigned, measured at 85 degrees Celsius, equivalent to + * 358.15 kelvin. + */ + ADC_CAL_TEMPSENSE, + }; + +/** \name ADC channel masks */ +/** @{ */ + +#define ADC_CH0 (1U << 0) /**< ADC channel 0. */ + +#if XMEGA_A || XMEGA_AU || defined(__DOXYGEN__) +# define ADC_CH1 (1U << 1) /**< ADC channel 1. */ +# define ADC_CH2 (1U << 2) /**< ADC channel 2. */ +# define ADC_CH3 (1U << 3) /**< ADC channel 3. */ +#endif + +/** @} */ + +/** \name Internal ADC input masks */ +/** @{ */ + +#define ADC_INT_TEMPSENSE ADC_TEMPREF_bm /**< Temperature sensor. */ +#define ADC_INT_BANDGAP ADC_BANDGAP_bm /**< Bandgap reference. */ + +/** @} */ + +/** + * \brief ADC conversion trigger settings + * + * \note The choice in conversion triggers varies between device families. + * Refer to the device manual for detailed information. + */ + enum adc_trigger + { + /** Manually triggered conversions */ + ADC_TRIG_MANUAL, + + /** Freerun mode conversion */ + ADC_TRIG_FREERUN, + + /** + * \brief Event-triggered conversions on individual channels + * Pairs each event channel with an ADC channel. + * \note The maximum base event channel that can be used is determined + * by the number of channels to trigger conversions on. + */ + ADC_TRIG_EVENT_SINGLE, + +#if ADC_NR_OF_CHANNELS > 1 + /** + * \brief Freerunning conversion sweeps + * \note These will start as soon as the ADC is enabled. + */ + ADC_TRIG_FREERUN_SWEEP, + + /** + * \brief Event-triggered conversion sweeps + * \note Only the base event channel is used in this mode. + */ + ADC_TRIG_EVENT_SWEEP, +#endif + + /** + * \brief Event-triggered, synchronized conversion sweeps + * \note Only the base event channel is used in this mode. + */ + ADC_TRIG_EVENT_SYNCSWEEP, + }; + +/** \brief ADC signedness settings */ + enum adc_sign + { + ADC_SIGN_OFF, /**< Unsigned conversions. */ + ADC_SIGN_ON = ADC_CONMODE_bm, /**< Signed conversions. */ + }; + +/** \brief ADC resolution settings */ + enum adc_resolution + { + /** 8-bit resolution, right-adjusted. */ + ADC_RES_8 = ADC_RESOLUTION_8BIT_gc, + /** 12-bit resolution, right-adjusted. */ + ADC_RES_12 = ADC_RESOLUTION_12BIT_gc, + /** 12-bit resolution, left-adjusted. */ + ADC_RES_12_LEFT = ADC_RESOLUTION_LEFT12BIT_gc, +#if XMEGA_E + + /** More than 12-bit resolution. + * Must be used when adcch_enable_averaging() or + * adcch_enable_oversampling() is used. + */ + ADC_RES_MT12 = ADC_RESOLUTION_MT12BIT_gc, +#endif + }; + +/** + * \brief ADC reference settings + * + * \note The choice in voltage reference varies between device families. + * Refer to the device manual for detailed information. + */ + enum adc_reference + { + /** Internal 1 V from bandgap reference. */ + ADC_REF_BANDGAP = ADC_REFSEL_INT1V_gc, + /** VCC divided by 1.6. */ + ADC_REF_VCC = ADC_REFSEL_INTVCC_gc, + /** External reference on AREFA pin. */ + ADC_REF_AREFA = ADC_REFSEL_AREFA_gc, +#if XMEGA_E + /** External reference on AREFD pin. */ + ADC_REF_AREFD = ADC_REFSEL_AREFD_gc, +#else + /** External reference on AREFB pin. */ + ADC_REF_AREFB = ADC_REFSEL_AREFB_gc, +#endif + /** VCC divided by 2. */ + ADC_REF_VCCDIV2 = ADC_REFSEL_VCCDIV2_gc, + }; + +/** \name Internal functions for driver */ +/** @{ */ + +/** + * \internal + * \brief Get ADC channel pointer from channel mask + * + * \param adc Pointer to ADC module. + * \param ch_mask Mask of ADC channel(s): + * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel + * can be given in mask) + * + * \return Pointer to ADC channel + */ + __always_inline ADC_CH_t *adc_get_channel (ADC_t * adc, uint8_t ch_mask); + + __always_inline ADC_CH_t *adc_get_channel (ADC_t * adc, uint8_t ch_mask) + { + uint8_t index = 0; + + Assert (ch_mask & ((1 << ADC_NR_OF_CHANNELS) - 1)); + + /* Use a conditional inline ctz for optimization. */ +#if ADC_NR_OF_CHANNELS > 4 + if (!(ch_mask & 0x0f)) + { + index += 4; + ch_mask >>= 4; + } +#endif +#if ADC_NR_OF_CHANNELS > 2 + if (!(ch_mask & 0x03)) + { + index += 2; + ch_mask >>= 2; + } +#endif +#if ADC_NR_OF_CHANNELS > 1 + if (!(ch_mask & 0x01)) + { + index++; + } +#endif + + return (ADC_CH_t *) (&adc->CH0 + index); + } + +/** @} */ + +#if defined(CONFIG_ADC_CALLBACK_ENABLE) || defined(__DOXYGEN__) +/** \name ADC interrupt callback function */ +/** @{ */ + +/** + * \def CONFIG_ADC_CALLBACK_ENABLE + * \brief Configuration symbol to enable callback on ADC interrupts + * + * Define this symbol in \ref conf_adc.h to enable callbacks on ADC interrupts. + * A function of type \ref adc_callback_t must be defined by the user, and the + * driver be configured to use it with \ref adc_set_callback. + */ +#if !defined(CONFIG_ADC_CALLBACK_ENABLE) || defined(__DOXYGEN__) +# define CONFIG_ADC_CALLBACK_ENABLE +#endif + +/** + * \def CONFIG_ADC_CALLBACK_TYPE + * \brief Configuration symbol for datatype of result parameter for callback + * + * Define the datatype of the ADC conversion result parameter for callback + * functions. This should be defined according to the signedness and resolution + * of the conversions: + * - \c int16_t for signed, 12-bit + * - \c uint16_t for unsigned, 12-bit (the default type) + * - \c int8_t for signed, 8-bit + * - \c uint8_t for unsigned, 8-bit + * + * Define this in \ref conf_adc.h if the default datatype is not desired. + */ +#if !defined(CONFIG_ADC_CALLBACK_TYPE) || defined(__DOXYGEN__) +# define CONFIG_ADC_CALLBACK_TYPE uint16_t +#endif + +/** Datatype of ADC conversion result parameter for callback */ + typedef CONFIG_ADC_CALLBACK_TYPE adc_result_t; + +/** + * \brief ADC interrupt callback function pointer + * + * \param adc Pointer to ADC module. + * \param ch_mask Mask of ADC channel(s): + * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel + * can be given in mask) + * \param res ADC conversion result. + */ + typedef void (*adc_callback_t) (ADC_t * adc, uint8_t ch_mask, + adc_result_t res); + + void adc_set_callback (ADC_t * adc, adc_callback_t callback); + +/** @} */ +#endif + +/** \name ADC module management */ +/** @{ */ + + void adc_enable (ADC_t * adc); + void adc_disable (ADC_t * adc); + bool adc_is_enabled (ADC_t * adc); + +/** + * \brief Start one-shot conversion on ADC channel(s) + * + * \param adc Pointer to ADC module. + * \param ch_mask Mask of ADC channel(s): + * \arg \c ADC_CHn , where \c n specifies the channel. (These can be OR'ed + * together.) + * + * \note The ADC must be enabled for this function to have any effect. + */ + static inline void adc_start_conversion (ADC_t * adc, uint8_t ch_mask) + { + irqflags_t flags = cpu_irq_save (); +#if !XMEGA_E + adc->CTRLA |= ch_mask << ADC_CH0START_bp; +#else + adc->CTRLA |= ch_mask << ADC_START_bp; +#endif + cpu_irq_restore (flags); + } + +/** + * \brief Get result from ADC channel + * + * Gets the latest conversion result from the ADC channel. + * + * \param adc Pointer to ADC module. + * \param ch_mask Mask of ADC channel(s): + * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel + * can be given in mask) + * + * \return Latest conversion result of ADC channel. Signedness does not matter. + * + * \note This macro does not protect the 16-bit read from interrupts. If an + * interrupt may do a 16-bit read or write to the ADC while this macro is + * executing, interrupts \a must be temporarily disabled to avoid corruption of + * the read. + */ +#define adc_get_result(adc, ch_mask) (adc_get_channel(adc, ch_mask)->RES) + +/** + * \brief Get signed result from ADC channel + * + * Returns the latest conversion result from the ADC channel as a signed type, + * with interrupt protection of the 16-bit read. + * + * \param adc Pointer to ADC module. + * \param ch_mask Mask of ADC channel(s): + * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel + * can be given in mask) + * + * \return Latest conversion result of ADC channel, as signed 16-bit integer. + */ + static inline int16_t adc_get_signed_result (ADC_t * adc, uint8_t ch_mask) + { + int16_t val; + irqflags_t flags; + ADC_CH_t *adc_ch; + + adc_ch = adc_get_channel (adc, ch_mask); + + flags = cpu_irq_save (); + val = adc_ch->RES; + cpu_irq_restore (flags); + + return val; + } + +/** + * \brief Get unsigned result from ADC channel + * + * Returns the latest conversion result from the ADC channel as an unsigned + * type, with interrupt protection of the 16-bit read. + * + * \param adc Pointer to ADC module. + * \param ch_mask Mask of ADC channel(s): + * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel + * can be given in mask) + * + * \return Latest conversion result of ADC channel, as unsigned 16-bit integer. + */ + static inline uint16_t adc_get_unsigned_result (ADC_t * adc, + uint8_t ch_mask) + { + uint16_t val; + irqflags_t flags; + ADC_CH_t *adc_ch; + + adc_ch = adc_get_channel (adc, ch_mask); + + flags = cpu_irq_save (); + val = adc_ch->RES; + cpu_irq_restore (flags); + + return val; + } + +/** + * \brief Get interrupt flag of ADC channel(s) + * + * Returns the interrupt flag of the masked channels. The meaning of the + * interrupt flag depends on what mode the individual channels are in. + * + * \param adc Pointer to ADC module. + * \param ch_mask Mask of ADC channel(s): + * \arg \c ADC_CHn , where \c n specifies the channel. (These can be OR'ed + * together.) + * + * \return Mask with interrupt flags. + */ + static inline uint8_t adc_get_interrupt_flag (ADC_t * adc, uint8_t ch_mask) + { + return (adc->INTFLAGS >> ADC_CH0IF_bp) & ch_mask; + } + +/** + * \brief Clear interrupt flag of ADC channel(s) + * + * \param adc Pointer to ADC module. + * \param ch_mask Mask of ADC channel(s): + * \arg \c ADC_CHn , where \c n specifies the channel. (These can be OR'ed + * together.) + * + * \note The ADC must be enabled for this function to have any effect. + */ + static inline void adc_clear_interrupt_flag (ADC_t * adc, uint8_t ch_mask) + { + adc->INTFLAGS = ch_mask << ADC_CH0IF_bp; + } + +/** + * \brief Wait for interrupt flag of ADC channel(s) + * + * Waits for the interrupt flag of the specified channel(s) to be set, then + * clears it before returning. If several channels are masked, the function will + * wait for \a all interrupt flags to be set. + * + * \param adc Pointer to ADC module. + * \param ch_mask Mask of ADC channel(s): + * \arg \c ADC_CHn , where \c n specifies the channel. (These can be OR'ed + * together.) + */ + static inline void adc_wait_for_interrupt_flag (ADC_t * adc, + uint8_t ch_mask) + { + do + { + } + while (adc_get_interrupt_flag (adc, ch_mask) != ch_mask); + adc_clear_interrupt_flag (adc, ch_mask); + } + +/** + * \brief Flush the ADC + * + * Forces the ADC to abort any ongoing conversions and restart its clock on the + * next peripheral clock cycle. Pending conversions are started after the + * clock reset. + * + * \param adc Pointer to ADC module. + * + * \note The ADC must be enabled for this function to have any effect. + */ + static inline void adc_flush (ADC_t * adc) + { + irqflags_t flags = cpu_irq_save (); + adc->CTRLA |= ADC_FLUSH_bm; + cpu_irq_restore (flags); + } + +/** + * \brief Set compare value directly to ADC + * + * Sets the compare value directly to the ADC, for quick access while the ADC is + * enabled. + * + * \param adc Pointer to ADC module. + * \param val Compare value to set, either signed or unsigned. + * + * \note The ADC must be enabled for this function to have any effect. + */ +#define adc_set_compare_value(adc, val) \ + do { \ + irqflags_t ATPASTE2(adc_flags, __LINE__) = cpu_irq_save(); \ + (adc)->CMP = val; \ + cpu_irq_restore(ATPASTE2(adc_flags, __LINE__)); \ + } \ + while (0) + +/** + * \brief Get compare value directly from ADC + * + * Gets the compare value directly from the ADC, for quick access while the ADC + * is enabled. + * + * \param adc Pointer to ADC module. + * + * \return Current compare value of the ADC. Signedness does not matter. + * + * \note This macro does not protect the 16-bit read from interrupts. If an + * interrupt may do a 16-bit read or write to the ADC while this macro is + * executing, interrupts \a must be temporarily disabled to avoid corruption of + * the read. + */ +#define adc_get_compare_value(adc) ((adc)->CMP) + +/** + * \brief Get signed compare value directly from ADC + * + * Gets the signed compare value directly from the ADC, with interrupt + * protection of the 16-bit read, for quick access while the ADC is enabled. + * + * \param adc Pointer to ADC module. + */ + static inline int16_t adc_get_signed_compare_value (ADC_t * adc) + { + int16_t val; + irqflags_t flags; + + flags = cpu_irq_save (); + val = adc->CMP; + cpu_irq_restore (flags); + + return val; + } + +/** + * \brief Get unsigned compare value directly from ADC + * + * Gets the unsigned compare value directly from the ADC, with interrupt + * protection of the 16-bit read, for quick access while the ADC is enabled. + * + * \param adc Pointer to ADC module. + */ + static inline uint16_t adc_get_unsigned_compare_value (ADC_t * adc) + { + uint16_t val; + irqflags_t flags; + + flags = cpu_irq_save (); + val = adc->CMP; + cpu_irq_restore (flags); + + return val; + } + +#if XMEGA_E + +/** + * \brief Set sample time value directly to ADC + * + * Sets the sample time value directly to the ADC, for quick access while the + * ADC is enabled. + * + * \param adc Pointer to ADC module. + * \param val Sample time value to set. + * + * \note The ADC must be enabled for this function to have any effect. + */ + static inline void adc_set_sample_value (ADC_t * adc, uint8_t val) + { + irqflags_t flags; + + flags = cpu_irq_save (); + adc->SAMPCTRL = (uint8_t) val; + cpu_irq_restore (flags); + } + +/** + * \brief Get sample time value directly from ADC + * + * Gets the sample time value directly from the ADC, for quick access while the + * ADC is enabled. + * + * \param adc Pointer to ADC module. + * + * \return Current sample time value of the ADC. + * + * \note This macro does not protect the 8-bit read from interrupts. If an + * interrupt may do a 8-bit read or write to the ADC while this macro is + * executing, interrupts \a must be temporarily disabled to avoid corruption of + * the read. + */ + static inline uint8_t adc_get_sample_value (ADC_t * adc) + { + return adc->SAMPCTRL; + } + +#endif + +/** + * \brief Get calibration data + * + * \param cal Identifier for calibration data to get. + */ + static inline uint16_t adc_get_calibration_data (enum adc_calibration_data + cal) + { + uint16_t data; + + switch (cal) + { +#ifdef ADCA + case ADC_CAL_ADCA: + data = nvm_read_production_signature_row (ADCACAL1); + data <<= 8; + data |= nvm_read_production_signature_row (ADCACAL0); + break; +#endif + +#ifdef ADCB + case ADC_CAL_ADCB: + data = nvm_read_production_signature_row (ADCBCAL1); + data <<= 8; + data |= nvm_read_production_signature_row (ADCBCAL0); + break; +#endif + +#if defined(ADCA) || defined(ADCB) + case ADC_CAL_TEMPSENSE: + data = nvm_read_production_signature_row (TEMPSENSE1); + data <<= 8; + data |= nvm_read_production_signature_row (TEMPSENSE0); + break; +#endif + + default: + Assert (0); + data = 0; + } + + return data; + } + +/** @} */ + +/** \name ADC module configuration */ +/** @{ */ + + void adc_write_configuration (ADC_t * adc, const struct adc_config *conf); + void adc_read_configuration (ADC_t * adc, struct adc_config *conf); + +/** + * \brief Set ADC prescaler to get desired clock rate + * + * Sets the ADC prescaling so that its clock rate becomes _at most_ + * \a clk_adc_hz. This is done by computing the ratio of the peripheral clock + * rate to the desired ADC clock rate, and rounding it upward to the nearest + * prescaling factor. + * + * \param conf Pointer to ADC module configuration. + * \param clk_adc Desired ADC clock rate. + * + * \note The sample rate is not determined solely by the ADC clock rate for all + * devices. Setting the current limit mode on some devices will also affect the + * maximum ADC sampling rate. Refer to the device manual for detailed + * information on conversion timing and/or the current limitation mode. + */ + static inline void adc_set_clock_rate (struct adc_config *conf, + uint32_t clk_adc) + { + uint32_t clk_per; + uint16_t ratio; + uint8_t psc; + + Assert (clk_adc); +#if XMEGA_A || XMEGA_AU + Assert (clk_adc <= 2000000UL); +#elif XMEGA_D + Assert (clk_adc <= 1400000UL); +#elif XMEGA_B || XMEGA_C || XMEGA_E + Assert (clk_adc <= 1800000UL); +#endif + + clk_per = sysclk_get_per_hz (); + ratio = clk_per / clk_adc; + + /* Round ratio up to the nearest prescaling factor. */ + if (ratio <= 4) + { + psc = ADC_PRESCALER_DIV4_gc; + } + else if (ratio <= 8) + { + psc = ADC_PRESCALER_DIV8_gc; + } + else if (ratio <= 16) + { + psc = ADC_PRESCALER_DIV16_gc; + } + else if (ratio <= 32) + { + psc = ADC_PRESCALER_DIV32_gc; + } + else if (ratio <= 64) + { + psc = ADC_PRESCALER_DIV64_gc; + } + else if (ratio <= 128) + { + psc = ADC_PRESCALER_DIV128_gc; + } + else if (ratio <= 256) + { + psc = ADC_PRESCALER_DIV256_gc; + } + else + { + psc = ADC_PRESCALER_DIV512_gc; + } + + conf->prescaler = psc; + } + +/** + * \brief Set ADC conversion parameters + * + * Sets the signedness, resolution and voltage reference for conversions in the + * ADC module configuration. + * + * \param conf Pointer to ADC module configuration. + * \param sign Conversion signedness. + * \param res Resolution of conversions. + * \param ref Voltage reference to use. + */ + static inline void adc_set_conversion_parameters (struct adc_config *conf, + enum adc_sign sign, + enum adc_resolution res, + enum adc_reference ref) + { + /* Preserve all but conversion and resolution config. */ + conf->ctrlb &= ~(ADC_CONMODE_bm | ADC_RESOLUTION_gm); + conf->ctrlb |= (uint8_t) res | (uint8_t) sign; + + conf->refctrl &= ~ADC_REFSEL_gm; + conf->refctrl |= ref; + } + +/** + * \brief Set ADC conversion trigger + * + * Configures the conversion triggering of the ADC. + * + * For automatic triggering modes, the number of channels to start conversions + * on must be specified with \a nr_of_ch. The channel selection for these + * modes is incrementally inclusive, always starting with channel 0. + * + * For event triggered modes, the base event channel must also be specified with + * \a base_ev_ch. The event channels are assigned to the ADC channels in an + * incremental fashion \a without \a wrap-around (in single-trigger event mode). + * This means that the maximum base event channel that can be used is determined + * by the number of ADC channels to start conversions on, i.e., \a nr_of_ch. + * + * \param conf Pointer to ADC module configuration. + * \param trig Conversion trigger to set. + * \param nr_of_ch Number of ADC channels to trigger conversions on: + * \arg \c 1 - \c ADC_NR_OF_CHANNELS (must be non-zero). + * \param base_ev_ch Base event channel, if used. + */ + static inline void adc_set_conversion_trigger (struct adc_config *conf, + enum adc_trigger trig, + uint8_t nr_of_ch, + uint8_t base_ev_ch) + { + Assert (nr_of_ch); + Assert (nr_of_ch <= ADC_NR_OF_CHANNELS); +#if XMEGA_A || XMEGA_AU || XMEGA_E + Assert (base_ev_ch <= 7); +#elif XMEGA_B || XMEGA_C || XMEGA_D + Assert (base_ev_ch <= 3); +#endif + + switch (trig) + { + case ADC_TRIG_MANUAL: + conf->ctrlb &= ~ADC_FREERUN_bm; + conf->evctrl = ADC_EVACT_NONE_gc; + break; + + case ADC_TRIG_EVENT_SINGLE: + conf->ctrlb &= ~ADC_FREERUN_bm; + conf->evctrl = (base_ev_ch << ADC_EVSEL_gp) | + (nr_of_ch << ADC_EVACT_gp); + break; + + case ADC_TRIG_FREERUN: + conf->ctrlb |= ADC_FREERUN_bm; + break; + +#if ADC_NR_OF_CHANNELS > 1 + case ADC_TRIG_FREERUN_SWEEP: + conf->ctrlb |= ADC_FREERUN_bm; + conf->evctrl = (nr_of_ch - 1) << ADC_SWEEP_gp; + break; + + case ADC_TRIG_EVENT_SWEEP: + conf->ctrlb &= ~ADC_FREERUN_bm; + conf->evctrl = (nr_of_ch - 1) << ADC_SWEEP_gp | + (base_ev_ch << ADC_EVSEL_gp) | ADC_EVACT_SWEEP_gc; + break; +#endif + + case ADC_TRIG_EVENT_SYNCSWEEP: + conf->ctrlb &= ~ADC_FREERUN_bm; + conf->evctrl = +#if ADC_NR_OF_CHANNELS > 1 + ((nr_of_ch - 1) << ADC_SWEEP_gp) | +#endif + (base_ev_ch << ADC_EVSEL_gp) | ADC_EVACT_SYNCSWEEP_gc; + break; + + default: + Assert (0); + } + } + +#if ADC_NR_OF_CHANNELS > 1 + +/** + * \brief Set DMA request group + * + * Configures the DMA group request for the specified number of ADC channels. + * The channel group selection is incrementally inclusive, always starting with + * channel 0. + * + * \param conf Pointer to ADC module configuration. + * \param nr_of_ch Number of channels for group request: + * \arg 0 to disable. + * \arg 2, 3 or 4 to enable. + * + * \note The number of channels in the DMA request group cannot be 1. + * \note Not all device families feature this setting. + */ + static inline void adc_set_dma_request_group (struct adc_config *conf, + uint8_t nr_of_ch) + { + Assert (nr_of_ch <= ADC_NR_OF_CHANNELS); + Assert (nr_of_ch != 1); + + if (nr_of_ch) + { + conf->ctrla = (nr_of_ch - 1) << ADC_DMASEL_gp; + } + else + { + conf->ctrla = ADC_DMASEL_OFF_gc; + } + } + +#endif + +/** + * \brief Enable internal ADC input + * + * \param conf Pointer to ADC module configuration. + * \param int_inp Internal input to enable: + * \arg \c ADC_INT_TEMPSENSE for temperature sensor. + * \arg \c ADC_INT_BANDGAP for bandgap reference. + */ + static inline void adc_enable_internal_input (struct adc_config *conf, + uint8_t int_inp) + { + conf->refctrl |= int_inp; + } + +/** + * \brief Disable internal ADC input + * + * \param conf Pointer to ADC module configuration. + * \param int_inp Internal input to disable: + * \arg \c ADC_INT_TEMPSENSE for temperature sensor. + * \arg \c ADC_INT_BANDGAP for bandgap reference. + */ + static inline void adc_disable_internal_input (struct adc_config *conf, + uint8_t int_inp) + { + conf->refctrl &= ~int_inp; + } + +#if XMEGA_AU || defined(__DOXYGEN__) +/** \brief ADC gain stage impedance settings */ + enum adc_gainstage_impmode + { + /** High impedance sources */ + ADC_GAIN_HIGHIMPEDANCE, + /** Low impedance sources */ + ADC_GAIN_LOWIMPEDANCE, + }; + +/** + * \brief Set ADC gain stage impedance mode + * + * \param conf Pointer to ADC module configuration. + * \param impmode Gain stage impedance mode. + * + * \note Not all device families feature this setting. + */ + static inline void adc_set_gain_impedance_mode (struct adc_config *conf, + enum adc_gainstage_impmode + impmode) + { + switch (impmode) + { + case ADC_GAIN_HIGHIMPEDANCE: + conf->ctrlb &= ~ADC_IMPMODE_bm; + break; + + case ADC_GAIN_LOWIMPEDANCE: + conf->ctrlb |= ADC_IMPMODE_bm; + break; + + default: + Assert (0); + } + } + +#endif + +#if !XMEGA_A +/** \brief ADC current limit settings */ + enum adc_current_limit + { + /** No current limit */ + ADC_CURRENT_LIMIT_NO, + /** Low current limit, max sampling rate 1.5 MSPS */ + ADC_CURRENT_LIMIT_LOW, + /** Medium current limit, max sampling rate 1 MSPS */ + ADC_CURRENT_LIMIT_MED, + /** High current limit, max sampling rate 0.5 MSPS */ + ADC_CURRENT_LIMIT_HIGH + }; + +/** + * \brief Set ADC current limit + * + * Set the current limit mode for the ADC module. This setting affects the max + * sampling rate of the ADC. + * + * \note See the device datasheet and manual for detailed information about + * current consumption and sample rate limit. + * + * \param conf Pointer to ADC module configuration. + * \param currlimit Current limit setting. + * + * \note Not all device families feature this setting. + */ + static inline void adc_set_current_limit (struct adc_config *conf, + enum adc_current_limit currlimit) + { + conf->ctrlb &= ~ADC_CURRLIMIT_gm; + + switch (currlimit) + { + case ADC_CURRENT_LIMIT_NO: + conf->ctrlb |= ADC_CURRLIMIT_NO_gc; + break; + + case ADC_CURRENT_LIMIT_LOW: + conf->ctrlb |= ADC_CURRLIMIT_LOW_gc; + break; + + case ADC_CURRENT_LIMIT_MED: + conf->ctrlb |= ADC_CURRLIMIT_MED_gc; + break; + + case ADC_CURRENT_LIMIT_HIGH: + conf->ctrlb |= ADC_CURRLIMIT_HIGH_gc; + break; + + default: + Assert (0); + } + } + +#endif + +/** + * \brief Set ADC compare value in configuration + * + * \param conf Pointer to ADC module configuration. + * \param val Compare value to set. + */ +#define adc_set_config_compare_value(conf, val) \ + do { \ + conf->cmp = (uint16_t)val; \ + } \ + while (0) + +/** + * \brief Get ADC compare value from configuration + * + * \param conf Pointer to ADC module configuration. + */ +#define adc_get_config_compare_value(conf) (conf->cmp) + +#if XMEGA_E + +/** + * \brief Set ADC sample time value in configuration + * + * \param conf Pointer to ADC module configuration. + * \param val Sample time value to set. + */ +#define adc_set_config_sample_value(conf, val) \ + do { \ + conf->sampctrl = (uint8_t)val; \ + } \ + while (0) + +/** + * \brief Get ADC sample time value from configuration + * + * \param conf Pointer to ADC module configuration. + */ +#define adc_get_config_sample_value(conf) (conf->sampctrl) +#endif + +/** @} */ + +/** @} */ + +/** + * \defgroup adc_channel_group ADC channel + * + * Management and configuration functions for the individual ADC channels. + * + * The API functions and definitions can be divided in two groups: + * - channel management: direct access for getting conversion result. + * - channel configuration: create/change configurations and write/read them + * to/from ADC channels. + * + * @{ + */ + +/** + * \brief Default ADC channel interrupt level + * + * \note To override the channel interrupt level, define this symbol as the + * desired level in \ref conf_adc.h. + */ +#if !defined(CONFIG_ADC_INTLVL) || defined(__DOXYGEN__) +# define CONFIG_ADC_INTLVL ADC_CH_INTLVL_LO_gc +#endif + +/** ADC channel configuration */ + struct adc_channel_config + { + uint8_t ctrl; + uint8_t muxctrl; + uint8_t intctrl; + uint8_t scan; +#if XMEGA_E + uint8_t corrctrl; + uint8_t offsetcorr0; + uint8_t offsetcorr1; + uint8_t gaincorr0; + uint8_t gaincorr1; + uint8_t avgctrl; +#endif + }; + +/** + * \brief ADC channel positive input + * + * Identifies the external and internal signals that can be used as positive + * input to the ADC channels. + */ + enum adcch_positive_input + { + ADCCH_POS_PIN0, + ADCCH_POS_PIN1, + ADCCH_POS_PIN2, + ADCCH_POS_PIN3, + ADCCH_POS_PIN4, + ADCCH_POS_PIN5, + ADCCH_POS_PIN6, + ADCCH_POS_PIN7, + ADCCH_POS_PIN8, + ADCCH_POS_PIN9, + ADCCH_POS_PIN10, + ADCCH_POS_PIN11, + ADCCH_POS_PIN12, + ADCCH_POS_PIN13, + ADCCH_POS_PIN14, + ADCCH_POS_PIN15, + + /** \name Internal inputs. */ + /** @{ */ + ADCCH_POS_TEMPSENSE, /**< Temperature sensor. */ + ADCCH_POS_BANDGAP, /**< Bandgap reference. */ + ADCCH_POS_SCALED_VCC, /**< VCC scaled down by 10. */ +#if XMEGA_A || XMEGA_AU || XMEGA_E || defined(__DOXYGEN__) + ADCCH_POS_DAC, /**< DAC output. */ +#endif + /** @} */ + }; + +/** + * \brief ADC channel negative input + * + * Identifies the signals that can be used as negative input to the ADC channels + * in differential mode. Some of the input signals are only available with + * certain gain settings, e.g., 1x gain. + * + * \note The ADC must be set in signed mode to use differential measurements. + * For single-ended measurements, ADDCH_NEG_NONE should be specified as negative + * input. + * + * \note Pad and internal GND are not available on all devices. See the device + * manual for an overview of available input signals. + */ + enum adcch_negative_input + { + /** \name Input pins for differential measurements with 1x gain. */ + /** @{ */ + /** ADC0 pin */ + ADCCH_NEG_PIN0, + /** ADC1 pin */ + ADCCH_NEG_PIN1, + /** ADC2 pin */ + ADCCH_NEG_PIN2, + /** ADC3 pin */ + ADCCH_NEG_PIN3, + /** @} */ + + /** \name Input pins for differential measurements with any gain. */ + /** @{ */ + /** ADC4 pin */ + ADCCH_NEG_PIN4, + /** ADC5 pin */ + ADCCH_NEG_PIN5, + /** ADC6 pin */ + ADCCH_NEG_PIN6, + /** ADC7 pin */ + ADCCH_NEG_PIN7, + /** @} */ + + /** \name GND signals for differential measurements. */ + /** @{ */ + /** PAD ground */ + ADCCH_NEG_PAD_GND, + /** Internal ground */ + ADCCH_NEG_INTERNAL_GND, + /** @} */ + + /** Single ended mode */ + ADCCH_NEG_NONE, + }; + +/** \brief ADC channel interrupt modes */ + enum adcch_mode + { + /** Set interrupt flag when conversions complete. */ + ADCCH_MODE_COMPLETE = ADC_CH_INTMODE_COMPLETE_gc, + /** Set interrupt flag when conversion result is below compare value. */ + ADCCH_MODE_BELOW = ADC_CH_INTMODE_BELOW_gc, + /** Set interrupt flag when conversion result is above compare value. */ + ADCCH_MODE_ABOVE = ADC_CH_INTMODE_ABOVE_gc, + }; + +/** \name ADC channel configuration */ +/** @{ */ + + void adcch_write_configuration (ADC_t * adc, uint8_t ch_mask, + const struct adc_channel_config *ch_conf); + void adcch_read_configuration (ADC_t * adc, uint8_t ch_mask, + struct adc_channel_config *ch_conf); + +/** Force enabling of gainstage with unity gain. */ +#define ADCCH_FORCE_1X_GAINSTAGE 0xff + +/** + * \internal + * \brief Get ADC channel setting for specified gain + * + * Returns the setting that corresponds to specified gain. + * + * \param gain Valid gain factor for the measurement. + * + * \return Gain setting of type ADC_CH_GAIN_t. + */ + static inline uint8_t adcch_get_gain_setting (uint8_t gain) + { + switch (gain) + { + case 0: + return ADC_CH_GAIN_DIV2_gc; + + case 1: + return ADC_CH_GAIN_1X_gc; + + case 2: + return ADC_CH_GAIN_2X_gc; + + case 4: + return ADC_CH_GAIN_4X_gc; + + case 8: + return ADC_CH_GAIN_8X_gc; + + case 16: + return ADC_CH_GAIN_16X_gc; + + case 32: + return ADC_CH_GAIN_32X_gc; + + case 64: + return ADC_CH_GAIN_64X_gc; + + case ADCCH_FORCE_1X_GAINSTAGE: + return ADC_CH_GAIN_1X_gc; + + default: + Assert (0); + return 0; + } + } + +/** + * \brief Set ADC channel input mode, multiplexing and gain + * + * Sets up an ADC channel's input mode and multiplexing according to specified + * input signals, as well as the gain. + * + * \param ch_conf Pointer to ADC channel configuration. + * \param pos Positive input signal. + * \param neg Negative input signal: + * \arg \c ADCCH_NEG_NONE for single-ended measurements. + * \arg \c ADCCH_NEG_PINn , where \c n specifies a pin, for differential + * measurements. + * \arg \c ADDCH_x_GND , where \c x specified pad or internal GND, for + * differential measurements. + * \param gain Gain factor for measurements: + * \arg 1 for single-ended or differential with pin 0, 1, 2 or 3, pad or + * internal GND as negative + * input. + * \arg 0 (0.5x), 1, 2, 4, 8, 16, 32 or 64 for differential with pin 4, 5, 6 or + * 7, pad or internal GND as negative input. + * \arg ADCCH_FORCE_1X_GAINSTAGE to force the gain stage to be enabled with + * unity gain for differential measurement. + * + * \note The GND signals are not available on all devices. Refer to the device + * manual for information on available input signals. + * + * \note With unity (1x) gain, some input selections may be possible both with + * and without the gain stage enabled. The driver will default to the + * configuration without gainstage to keep the current consumption as low as + * possible unless the user specifies \ref ADCCH_FORCE_1X_GAINSTAGE as \a gain. + */ + static inline void adcch_set_input (struct adc_channel_config *ch_conf, + enum adcch_positive_input pos, + enum adcch_negative_input neg, + uint8_t gain) + { + if (pos >= ADCCH_POS_TEMPSENSE) + { + /* Configure for internal input. */ + Assert (gain == 1); + Assert (neg == ADCCH_NEG_NONE); + + ch_conf->ctrl = ADC_CH_INPUTMODE_INTERNAL_gc; + ch_conf->muxctrl = (pos - ADCCH_POS_TEMPSENSE) << ADC_CH_MUXPOS_gp; + } + else if (neg == ADCCH_NEG_NONE) + { + /* Configure for single-ended measurement. */ + Assert (gain == 1); + + ch_conf->ctrl = ADC_CH_INPUTMODE_SINGLEENDED_gc; + ch_conf->muxctrl = pos << ADC_CH_MUXPOS_gp; + } + else if (neg <= ADCCH_NEG_PIN3) + { + /* Configure for differential measurement. + * Pins 0-3 can only be used for negative input if the gain + * stage is not used, i.e., unity gain (except XMEGA E). + */ +#if XMEGA_E + ch_conf->ctrl = adcch_get_gain_setting (gain) | + ADC_CH_INPUTMODE_DIFFWGAINL_gc; +#else + Assert (gain == 1); + ch_conf->ctrl = ADC_CH_INPUTMODE_DIFF_gc; +#endif + ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) | + (neg << ADC_CH_MUXNEG_gp); + } + else if (neg <= ADCCH_NEG_PIN7) + { + /* Configure for differential measurement. + * Pins 4-7 can be used for all gain settings, + * including unity gain, which is available even if + * the gain stage is active. + */ +#if XMEGA_E + ch_conf->ctrl = adcch_get_gain_setting (gain) | + ADC_CH_INPUTMODE_DIFFWGAINH_gc; +#else + ch_conf->ctrl = adcch_get_gain_setting (gain) | + ADC_CH_INPUTMODE_DIFFWGAIN_gc; +#endif + ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) | + ((neg - ADCCH_NEG_PIN4) << ADC_CH_MUXNEG_gp); + } + else + { + Assert ((neg == ADCCH_NEG_PAD_GND) || + (neg == ADCCH_NEG_INTERNAL_GND)); +#if XMEGA_E + + /* Configure for differential measurement through PAD GND or + * internal GND. + * DIFFWGAINH (INPUTMODE) is not used because it support + * only PAD GND. + */ + ch_conf->ctrl = ADC_CH_INPUTMODE_DIFFWGAINL_gc | + adcch_get_gain_setting (gain); + ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) | + ((neg == ADCCH_NEG_INTERNAL_GND) ? + ADC_CH_MUXNEGL_INTGND_gc : ADC_CH_MUXNEGL_GND_gc); +#else + + /* Configure for differential measurement through GND or + * internal GND. + * The bitmasks for the on-chip GND signals change when + * gain is enabled. To avoid unnecessary current consumption, + * do not enable gainstage for unity gain unless user explicitly + * specifies it with the ADCCH_FORCE_1X_GAINSTAGE macro. + */ + if (gain == 1) + { + ch_conf->ctrl = ADC_CH_INPUTMODE_DIFF_gc; + ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) | + ((neg == ADCCH_NEG_PAD_GND) ? + ADC_CH_MUXNEG_MODE10_GND_gc : ADC_CH_MUXNEG_MODE10_INTGND_gc); + } + else + { + ch_conf->ctrl = ADC_CH_INPUTMODE_DIFFWGAIN_gc | + adcch_get_gain_setting (gain); + ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) | + ((neg == ADCCH_NEG_INTERNAL_GND) ? + ADC_CH_MUXNEG_MODE11_INTGND_gc : ADC_CH_MUXNEG_MODE11_GND_gc); + } + +#endif + } + } + +/** + * \brief Set ADC channel 0 pin scan + * + * Sets the parameters for pin scan, which enables measurements on multiple, + * successive input pins without any reconfiguration between conversions. + * + * Pin scan works by adding a offset to the positive MUX setting to get the + * current input pin. The offset is incremented for each conversion, and is + * reset to 0 once a conversion with the maximum offset is done. + * + * \param ch_conf Pointer to the ADC channel configuration structure + * \param start_offset Initial offset to start pin scan at + * \arg \c 0 - \c max_offset + * \param max_offset Maximum offset for the pin scan + * \arg \c 0 to disable + * \arg \c 1 - \c 15 to enable + * + * \note Only the AVR XMEGA AU family features this setting. + * \note Pin scan is only available on ADC channel 0. + */ + static inline void adcch_set_pin_scan (struct adc_channel_config *ch_conf, + uint8_t start_offset, + uint8_t max_offset) + { + Assert (start_offset < 16); + Assert (max_offset < 16); + Assert (start_offset <= max_offset); + + ch_conf->scan = max_offset | (start_offset << ADC_CH_OFFSET_gp); + } + +/** + * \brief Set ADC channel interrupt mode + * + * \param ch_conf Pointer to ADC channel configuration. + * \param mode Interrupt mode to set. + */ + static inline void adcch_set_interrupt_mode (struct adc_channel_config + *ch_conf, enum adcch_mode mode) + { + ch_conf->intctrl &= ~ADC_CH_INTMODE_gm; + ch_conf->intctrl |= mode; + } + +/** + * \brief Enable interrupts on ADC channel + * + * \param ch_conf Pointer to ADC channel configuration. + */ + static inline void adcch_enable_interrupt (struct adc_channel_config + *ch_conf) + { + ch_conf->intctrl &= ~ADC_CH_INTLVL_gm; + ch_conf->intctrl |= CONFIG_ADC_INTLVL; + } + +/** + * \brief Disable interrupts on ADC channel + * + * \param ch_conf Pointer to ADC channel configuration. + */ + static inline void adcch_disable_interrupt (struct adc_channel_config + *ch_conf) + { + ch_conf->intctrl &= ~ADC_CH_INTLVL_gm; + ch_conf->intctrl |= ADC_CH_INTLVL_OFF_gc; + } + +#if XMEGA_E + +/** + * \brief Enable gain & offset corrections on ADC channel + * + * \param ch_conf Pointer to ADC channel configuration. + * \param offset_corr Offset correction value to set. + * \param expected_value Expected value for a specific input voltage + * \param captured_value Captured value for a specific input voltage + * + * \Note + * Gived "expected_value = captured_value = 1" to ignore the gain correction + * Gain correction is equal to "expected_value / captured_value" + */ + static inline void adcch_enable_correction (struct adc_channel_config + *ch_conf, uint16_t offset_corr, + uint16_t expected_value, + uint16_t captured_value) + { + uint32_t gain_corr; + + gain_corr = (2048L * expected_value) / captured_value; + ch_conf->offsetcorr0 = LSB (offset_corr); + ch_conf->offsetcorr1 = MSB (offset_corr); + ch_conf->gaincorr0 = LSB (gain_corr); + ch_conf->gaincorr1 = MSB (gain_corr); + ch_conf->corrctrl = ADC_CH_CORREN_bm; + } + +/** + * \brief Disable gain & offset correction on ADC channel + * + * \param ch_conf Pointer to ADC channel configuration. + */ + static inline void adcch_disable_correction (struct adc_channel_config + *ch_conf) + { + ch_conf->corrctrl = ADC_CH_CORREN_bp; + } + +/** \brief ADC channel sample number settings */ + enum adcch_sampnum + { + /** 2 samples to accumulate. */ + ADC_SAMPNUM_2X = ADC_SAMPNUM_2X_gc, + /** 4 samples to accumulate. */ + ADC_SAMPNUM_4X = ADC_SAMPNUM_4X_gc, + /** 8 samples to accumulate. */ + ADC_SAMPNUM_8X = ADC_SAMPNUM_8X_gc, + /** 16 samples to accumulate. */ + ADC_SAMPNUM_16X = ADC_SAMPNUM_16X_gc, + /** 32 samples to accumulate. */ + ADC_SAMPNUM_32X = ADC_SAMPNUM_32X_gc, + /** 64 samples to accumulate. */ + ADC_SAMPNUM_64X = ADC_SAMPNUM_64X_gc, + /** 128 samples to accumulate. */ + ADC_SAMPNUM_128X = ADC_SAMPNUM_128X_gc, + /** 256 samples to accumulate. */ + ADC_SAMPNUM_256X = ADC_SAMPNUM_256X_gc, + /** 512 samples to accumulate. */ + ADC_SAMPNUM_512X = ADC_SAMPNUM_512X_gc, + /** 1024 samples to accumulate. */ + ADC_SAMPNUM_1024X = ADC_SAMPNUM_1024X_gc, + }; + +/** + * \brief Enables ADC channel averaging + * + * Sets the parameters number of samples used during averaging. + * + * \param ch_conf Pointer to the ADC channel configuration structure + * \param sample Number of samples to accumulate + * + * \note Only the AVR XMEGA E family features this setting. + * \note Check that "ADC_RES_MT12" param is used + * in adc_set_conversion_parameters() call. + */ + static inline void adcch_enable_averaging (struct adc_channel_config + *ch_conf, + enum adcch_sampnum sample) + { + uint8_t rshift; + + Assert (sample >= ADC_SAMPNUM_2X); + + if (sample >= ADC_SAMPNUM_16X) + { + rshift = 4; + } + else if (sample == ADC_SAMPNUM_8X) + { + rshift = 3; + } + else if (sample == ADC_SAMPNUM_4X) + { + rshift = 2; + } + else + { + rshift = 1; + } + + ch_conf->avgctrl = sample | (rshift << ADC_CH_RIGHTSHIFT_gp); + } + +/** + * \brief Disables ADC channel averaging + * + * \param ch_conf Pointer to the ADC channel configuration structure + * + * \note Only the AVR XMEGA E family features this setting. + * \note Check that "ADC_RES_MT12" param is not used + * in adc_set_conversion_parameters() call. + */ + static inline void adcch_disable_averaging (struct adc_channel_config + *ch_conf) + { + ch_conf->avgctrl = 0; + } + +/** + * \brief Enables ADC channel over-sampling + * + * Sets the parameters number of samples and result resolution + * used during over-sampling. + * + * \param ch_conf Pointer to the ADC channel configuration structure + * \param sample Number of samples to accumulate + * \param resolution result resolution (12 bits to 16 bits) + * 15 bits maximum if sample = 8 + * 14 bits maximum if sample = 4 + * 13 bits maximum if sample = 2 + * + * \note Only the AVR XMEGA E family features this setting. + * \note Check that "ADC_RES_MT12" param is used + * in adc_set_conversion_parameters() call. + */ + static inline void adcch_enable_oversampling (struct adc_channel_config + *ch_conf, + enum adcch_sampnum sample, + uint8_t resolution) + { + uint8_t rshift; + + Assert ((resolution >= 12) && (resolution <= 16)); + + if (sample >= ADC_SAMPNUM_16X) + { + rshift = 4; + } + else if (sample == ADC_SAMPNUM_8X) + { + rshift = 3; + } + else if (sample == ADC_SAMPNUM_4X) + { + rshift = 2; + } + else + { + rshift = 1; + } + + Assert (rshift >= resolution - 12); + rshift -= resolution - 12; + ch_conf->avgctrl = sample | (rshift << ADC_CH_RIGHTSHIFT_gp); + } + +/** + * \brief Disables ADC channel over-sampling + * + * \param ch_conf Pointer to the ADC channel configuration structure + * + * \note Only the AVR XMEGA E family features this setting. + * \note Check that "ADC_RES_MT12" param is not used + * in adc_set_conversion_parameters() call. + */ + static inline void adcch_disable_oversampling (struct adc_channel_config + *ch_conf) + { + ch_conf->avgctrl = 0; + } + +#endif + +/** @} */ + +/** @} */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** + * \page adc_quickstart Quick start guide for XMEGA ADC + * + * This is the quick start guide for the \ref adc_group, with step-by-step + * instructions on how to configure and use the driver in a selection of use + * cases. + * + * The use cases are described with "setup" and "usage" sections, which each + * have "example code" and "workflow" subsections. This documentation first + * presents code fragments and function definitions along with instructions on + * where they can be placed, e.g., into the application C-file or the main() + * function, then follows up with explanations for all the lines of code. + * + * \section adc_use_cases Use cases + * + * In addition to the basic use case below, refer to the following use cases for + * demonstrations of the ADC's features: + * - \subpage adc_use_case_1 + * - \subpage adc_use_case_2 + * + * We recommend reading all the use cases for the sake of all the notes on + * considerations, limitations and other helpful details. + * + * \section adc_basic_use_case Basic use case + * + * In this basic use case, ADCA is configured for: + * - sampling on a single channel (0) + * - I/O pin as single-ended input (PA0) + * - unsigned conversions + * - 12-bit resolution + * - internal 1V reference + * - manual conversion triggering + * - polled operation (no interrupts) + * + * Completed conversions are detected by waiting for the relevant interrupt flag + * to get set. The ADC result is then stored in a local variable. + * + * \section adc_basic_use_case_setup Setup steps + * + * \subsection adc_basic_use_case_setup_code Example code + * + * Add to application C-file: + * \code + * #define MY_ADC ADCA + * #define MY_ADC_CH ADC_CH0 + * + * static void adc_init(void) + * { + * struct adc_config adc_conf; + * struct adc_channel_config adcch_conf; + * + * adc_read_configuration(&MY_ADC, &adc_conf); + * adcch_read_configuration(&MY_ADC, MY_ADC_CH, &adcch_conf); + * + * adc_set_conversion_parameters(&adc_conf, ADC_SIGN_OFF, ADC_RES_12, + * ADC_REF_BANDGAP); + * adc_set_conversion_trigger(&adc_conf, ADC_TRIG_MANUAL, 1, 0); + * adc_set_clock_rate(&adc_conf, 200000UL); + * + * adcch_set_input(&adcch_conf, ADCCH_POS_PIN0, ADCCH_NEG_NONE, 1); + * + * adc_write_configuration(&MY_ADC, &adc_conf); + * adcch_write_configuration(&MY_ADC, MY_ADC_CH, &adcch_conf); + * } + * \endcode + * + * Add to \c main(): + * \code + * sysclk_init(); + * adc_init(); + * \endcode + * + * \subsection adc_basic_use_case_setup_flow Workflow + * + * -# Add macros for the ADC and its channel to use, so they are easy to change: + * - \code + * #define MY_ADC ADCA + * #define MY_ADC_CH ADC_CH0 + * \endcode + * -# Create a function \c adc_init() to intialize the ADC: + * - \code + * static void adc_init(void) + * { + * // ... + * } + * \endcode + * -# Allocate configuration structs for the ADC and its channel: + * - \code + * struct adc_config adc_conf; + * struct adc_channel_config adcch_conf; + * \endcode + * -# Initialize the structs: + * - \code + * adc_read_configuration(&MY_ADC, &adc_conf); + * adcch_read_configuration(&MY_ADC, MY_ADC_CH, &adcch_conf); + * \endcode + * \attention This step must not be skipped because uninitialized structs + * may contain invalid configurations, thus giving unpredictable behavior. + * -# Set conversion parameters to unsigned, 12-bit and internal 1V reference: + * - \code + * adc_set_conversion_parameters(&adc_conf, ADC_SIGN_OFF, ADC_RES_12, + * ADC_REF_BANDGAP); + * \endcode + * \note Only single-ended input is possible with unsigned conversions. + * -# Set conversion trigger to manual triggering: + * - \code + * adc_set_conversion_trigger(&adc_conf, ADC_TRIG_MANUAL, 1, 0); + * \endcode + * \note The number of channels to trigger (1) and base event channel (0) + * don't affect operation in this trigger mode, but sane values should still be + * supplied. + * -# Set ADC clock rate to 200 KHz or less: + * - \code + * adc_set_clock_rate(&adc_conf, 200000UL); + * \endcode + * \note The driver attempts to set the ADC clock rate to the fastest + * possible without exceeding the specified limit. Refer to the applicable + * device datasheet and manual for details on maximum ADC clock rate. + * -# Set pin 0 on the associated port as the single-ended input: + * - \code + * adcch_set_input(&adcch_conf, ADCCH_POS_PIN0, ADCCH_NEG_NONE, 1); + * \endcode + * \note For single-ended input, the negative input must be none and the + * gain must be unity (1x). + * -# Write the configurations to ADC and channel: + * - \code + * adc_write_configuration(&MY_ADC, &adc_conf); + * adcch_write_configuration(&MY_ADC, MY_ADC_CH, &adcch_conf); + * \endcode + * -# Initialize the clock system: + * - \code sysclk_init(); \endcode + * \note The ADC driver requires the system clock driver to be + * initialized in order to compute the correct ADC clock rate in step 6. + * -# Call our ADC init function: + * - \code adc_init(); \endcode + * + * \section adc_basic_use_case_usage Usage steps + * + * \subsection adc_basic_use_case_usage_code Example code + * + * Add to, e.g., main-loop in application C-file: + * \code + * uint16_t result; + * + * adc_enable(&MY_ADC); + * + * adc_start_conversion(&MY_ADC, MY_ADC_CH); + * adc_wait_for_interrupt_flag(&MY_ADC, MY_ADC_CH); + * + * result = adc_get_result(&MY_ADC, MY_ADC_CH); + * \endcode + * + * \subsection adc_basic_use_case_usage_flow Workflow + * + * -# Allocate a variable to contain the ADC result: + * - \code uint16_t result; \endcode + * -# Enable the configured ADC: + * - \code adc_enable(&MY_ADC); \endcode + * -# Trigger a single conversion on the ADC channel: + * - \code adc_start_conversion(&MY_ADC, MY_ADC_CH); \endcode + * -# Wait for the channel's interrupt flag to get set, indicating a completed + * conversion: + * - \code adc_wait_for_interrupt_flag(&MY_ADC, MY_ADC_CH); \endcode + * \note The interrupt flags are set even if the interrupts are disabled. + * Further, this function will clear the interrupt flag after it has been set, + * so we do not need to clear it manually. + * -# Read out the result of the ADC channel: + * - \code result = adc_get_result(&MY_ADC, MY_ADC_CH); \endcode + * -# To do more conversions, go back to step 3. + */ + +/** + * \page adc_use_case_1 Free-running conversions with interrupt + * + * In this use case, ADCA is configured for: + * \li sampling on two channels (0 and 1) with respective inputs: + * - I/O pin as single-ended input (PA0) + * - two I/O pins as differential input w/ 2x gain (PA1 and PA5) + * \li signed conversions + * \li 12-bit resolution + * \li internal 1V reference + * \li free-running conversions + * \li interrupt-based conversion handling + * + * The ADC results are handled in an interrupt callback function which simply + * stores the result in one of two channel-specific, global variables. + * + * \note This use case assumes that the device has multiple ADC channels. Refer + * to the applicable device datasheet for information about the number of ADC + * channels. + * + * \section adc_use_case_1_setup Setup steps + * + * \subsection adc_use_case_1_setup_code Example code + * + * Ensure that \ref conf_adc.h contains: + * \code + * #define CONFIG_ADC_CALLBACK_ENABLE + * #define CONFIG_ADC_CALLBACK_TYPE int16_t + * \endcode + * + * Add to application C-file: + * \code + * #define MY_ADC ADCA + * + * int16_t ch0_result; + * int16_t ch1_result; + * + * static void adc_handler(ADC_t *adc, uint8_t ch_mask, adc_result_t result) + * { + * switch (ch_mask) { + * case ADC_CH0: + * ch0_result = result; + * break; + * + * case ADC_CH1: + * ch1_result = result; + * break; + * + * default: + * break; + * } + * } + * + * static void adc_init(void) + * { + * struct adc_config adc_conf; + * struct adc_channel_config adcch_conf; + * + * adc_read_configuration(&MY_ADC, &adc_conf); + * adcch_read_configuration(&MY_ADC, ADC_CH0, &adcch_conf); + * + * adc_set_conversion_parameters(&adc_conf, ADC_SIGN_ON, ADC_RES_12, + * ADC_REF_BANDGAP); + * adc_set_conversion_trigger(&adc_conf, ADC_TRIG_FREERUN_SWEEP, 2, 0); + * adc_set_clock_rate(&adc_conf, 5000UL); + * adc_set_callback(&MY_ADC, &adc_handler); + * adc_write_configuration(&MY_ADC, &adc_conf); + * + * adcch_enable_interrupt(&adcch_conf); + * adcch_set_input(&adcch_conf, ADCCH_POS_PIN0, ADCCH_NEG_NONE, 1); + * adcch_write_configuration(&MY_ADC, ADC_CH0, &adcch_conf); + * + * adcch_set_input(&adcch_conf, ADCCH_POS_PIN1, ADCCH_NEG_PIN5, 2); + * adcch_write_configuration(&MY_ADC, ADC_CH1, &adcch_conf); + * } + * \endcode + * + * Add to \c main(): + * \code + * sysclk_init(); + * adc_init(); + * pmic_init(); + * \endcode + * + * \subsection adc_use_case_1_setup_flow Workflow + * + * -# Define a macro for the ADC to use, in case we want to change it later: + * - \code #define MY_ADC ADCA \endcode + * -# Define global variables to contain the ADC result of each channel: + * - \code + * int16_t ch0_result; + * int16_t ch1_result; + * \endcode + * -# Create an ADC interrupt callback function that stores the results in the + * channels' respective global variables: + * - \code + * static void adc_handler(ADC_t *adc, uint8_t ch_mask, adc_result_t result) + * { + * switch (ch_mask) { + * case ADC_CH0: + * ch0_result = result; + * break; + * + * case ADC_CH1: + * ch1_result = result; + * break; + * + * default: + * break; + * } + * } + * \endcode + * \note Refer to \ref adc_callback_t for documentation on the interrupt + * callback function type. + * -# Create a function \c adc_init() to intialize the ADC: + * - \code + * static void adc_init(void) + * { + * // ... + * } + * \endcode + * -# Allocate configuration structs for ADC and channel, then initialize them: + * - \code + * struct adc_config adc_conf; + * struct adc_channel_config adcch_conf; + * + * adc_read_configuration(&MY_ADC, &adc_conf); + * adcch_read_configuration(&MY_ADC, ADC_CH0, &adcch_conf); + * \endcode + * -# Set signed, 12-bit conversions with internal 1V voltage reference: + * - \code + * adc_set_conversion_parameters(&adc_conf, ADC_SIGN_ON, ADC_RES_12, + * ADC_REF_BANDGAP); + * \endcode + * \note With signed, 12-bit conversion, 1 bit is used to indicate + * sign/polarity, so the resolution is halved in terms of Volt per LSB. + * -# Set free-running conversions on the first two ADC channels: + * - \code + * adc_set_conversion_trigger(&adc_conf, ADC_TRIG_FREERUN_SWEEP, 2, 0); + * \endcode + * \note The base event channel (0) does not affect operation in this + * mode. + * -# Set ADC clock rate to maximum 5 KHz: + * - \code adc_set_clock_rate(&adc_conf, 5000UL); \endcode + * \note In free-running mode, it is wise to reduce the ADC clock so that + * the device has time to handle the results, e.g., channel 0 does not complete + * a new conversion before channel 1's result has been handled. + * -# Set the interrupt callback function to use for the ADC: + * - \code adc_set_callback(&MY_ADC, &adc_handler); \endcode + * -# Write the configuration to the ADC: + * - \code adc_write_configuration(&MY_ADC, &adc_conf); \endcode + * -# Enable interrupts for the ADC channels: + * - \code adcch_enable_interrupt(&adcch_conf); \endcode + * -# Set up single-ended input from pin 0 on port A, then write the config to + * the first channel (0): + * - \code + * adcch_set_input(&adcch_conf, ADCCH_POS_PIN0, ADCCH_NEG_NONE, 1); + * adcch_write_configuration(&MY_ADC, ADC_CH0, &adcch_conf); + * \endcode + * -# Set up differential input from pins 1 and 5 on port A, with 2x gain, then + * write the config to the second channel (1): + * - \code + * adcch_set_input(&adcch_conf, ADCCH_POS_PIN1, ADCCH_NEG_PIN5, 2); + * adcch_write_configuration(&MY_ADC, ADC_CH1, &adcch_conf); + * \endcode + * \note Not all input and gain combinations are valid. Refer to + * \ref adcch_set_input() for documentation on the restrictions. + * -# Initialize the clock system, the ADC, and the PMIC since we will be using + * interrupts: + * - \code + * sysclk_init(); + * adc_init(); + * pmic_init(); + * \endcode + * \note The call to \ref pmic_init() does not enable interrupts globally, + * which must be done explicitly with \ref cpu_irq_enable(). + * + * \section adc_use_case_1_usage Usage steps + * + * \subsection adc_use_case_1_usage_code Example code + * + * Add to \c main.c(): + * \code + * cpu_irq_enable(); + * adc_enable(&MY_ADC); + * + * do { + * } while (true); + * \endcode + * + * \subsection adc_use_case_1_usage_flow Workflow + * -# Enable interrupts globally to allow the ADC interrupts to be handled: + * - \code cpu_irq_enable(); \endcode + * -# Enable the ADC to start conversions: + * - \code adc_enable(&MY_ADC); \endcode + * \note When configured for free-running conversions, the ADC will start + * doing conversions as soon as it is enabled, so we do not need to do it + * manually. + * -# Enter a busy-loop while interrupts handle the ADC results: + * - \code + * do { + * } while (true); + * \endcode + */ + +/** + * \page adc_use_case_2 Event-triggered conversions + * + * In this use case, ADCA is configured for: + * \li sampling on two channels (0 and 1) with respective inputs: + * - internal temperature sensor + * - internal bandgap reference + * \li unsigned conversions + * \li 12-bit resolution + * \li VCC/1.6 as voltage reference + * \li event-triggered conversions + * \li polled conversion handling + * + * Completed conversions are detected via non-blocking polling of the interrupt + * flags. The ADC results are stored into local variables as soon as they are + * available. + * + * A Timer/Counter is used to generate events that trigger the conversions. + * + * \note This use case assumes that the device has multiple ADC channels. Refer + * to the applicable device datasheet for information about the number of ADC + * channels. + * + * \section adc_use_case_2_setup Setup steps + * + * \subsection adc_use_case_2_setup_prereq Prerequisites + * + * This use case requires that the Timer/Counter driver is added to the project. + * + * \subsection adc_use_case_2_setup_code Example code + * + * Add to application C-file: + * \code + * #define MY_ADC ADCA + * #define MY_TIMER TCC0 + * + * static void evsys_init(void) + * { + * sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_EVSYS); + * EVSYS.CH3MUX = EVSYS_CHMUX_TCC0_OVF_gc; + * } + * + * static void tc_init(void) + * { + * tc_enable(&MY_TIMER); + * tc_set_wgm(&MY_TIMER, TC_WG_NORMAL); + * tc_write_period(&MY_TIMER, 200); + * tc_set_resolution(&MY_TIMER, 2000); + * } + * + * static void adc_init(void) + * { + * struct adc_config adc_conf; + * struct adc_channel_config adcch_conf; + * + * adc_read_configuration(&MY_ADC, &adc_conf); + * adcch_read_configuration(&MY_ADC, ADC_CH0, &adcch_conf); + * + * adc_set_conversion_parameters(&adc_conf, ADC_SIGN_OFF, ADC_RES_12, + * ADC_REF_VCC); + * adc_set_conversion_trigger(&adc_conf, ADC_TRIG_EVENT_SWEEP, 2, 3); + * adc_enable_internal_input(&adc_conf, ADC_INT_BANDGAP + * | ADC_INT_TEMPSENSE); + * adc_set_clock_rate(&adc_conf, 200000UL); + * adc_write_configuration(&MY_ADC, &adc_conf); + * + * adcch_set_input(&adcch_conf, ADCCH_POS_TEMPSENSE, ADCCH_NEG_NONE, 1); + * adcch_write_configuration(&MY_ADC, ADC_CH0, &adcch_conf); + * + * adcch_set_input(&adcch_conf, ADCCH_POS_BANDGAP, ADCCH_NEG_NONE, 1); + * adcch_write_configuration(&MY_ADC, ADC_CH1, &adcch_conf); + * } + * \endcode + * + * Add to \c main(): + * \code + * sysclk_init(); + * evsys_init(); + * tc_init(); + * adc_init(); + * \endcode + * + * \subsection adc_use_case_2_setup_flow Workflow + * + * -# Add macros for the ADC and the conversion trigger timer to use, so they + * are easy to change: + * - \code + * #define MY_ADC ADCA + * #define MY_TIMER TCC0 + * \endcode + * -# Create a function \c evsys_init() to intialize the event system clocks and + * to link the conversion timer to the correct event channel: + * - \code + * static void evsys_init(void) + * { + * // ... + * } + * \endcode + * -# Use the sysclk service to enable the clock to the event system: + * - \code sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_EVSYS); \endcode + * -# Connect the TCC0 overflow event to event channel 3: + * - \code EVSYS.CH3MUX = EVSYS_CHMUX_TCC0_OVF_gc; \endcode + * \note If the ADC trigger timer is changed from TCC0, the \c EVSYS_CHMUX_* + * mask here will also need to be altered. + * -# Create a function \c tc_init() to intialize the ADC trigger timer: + * - \code + * static void tc_init(void) + * { + * // ... + * } + * \endcode + * -# Enable the clock to the ADC trigger timer: + * - \code tc_enable(&MY_TIMER); \endcode + * -# Configure the ADC trigger timer in normal Waveform Generation mode: + * - \code tc_set_wgm(&MY_TIMER, TC_WG_NORMAL); \endcode + * -# Configure the ADC trigger timer period to overflow at 200 counts: + * - \code tc_write_period(&MY_TIMER, 200); \endcode + * -# Configure the ADC trigger timer resolution (frequency) for 2KHz: + * - \code tc_set_resolution(&MY_TIMER, 2000); \endcode + * -# Create a function \c adc_init() to intialize the ADC ready for + * conversions on channels 0 and 1, triggered by the event system: + * - \code + * static void adc_init(void) + * { + * // ... + * } + * \endcode + * -# Allocate configuration structs for ADC and channel, then initialize them: + * - \code + * struct adc_config adc_conf; + * struct adc_channel_config adcch_conf; + * + * adc_read_configuration(&MY_ADC, &adc_conf); + * adcch_read_configuration(&MY_ADC, ADC_CH0, &adcch_conf); + * \endcode + * -# Set unsigned, 12-bit conversions with internal VCC/1.6 voltage reference: + * - \code + * adc_set_conversion_parameters(&adc_conf, ADC_SIGN_OFF, ADC_RES_12, + * ADC_REF_VCC); + * \endcode + * -# Set event system triggered conversions on the first two ADC channels, + * with conversions triggered by event system channel 3: + * - \code + * adc_set_conversion_trigger(&adc_conf, ADC_TRIG_EVENT_SWEEP, 2, 3); + * \endcode + * \note The event system channel used here must match the channel linked to the + * conversion trigger timer set up earlier in \c tc_init(). + * -# Turn on the internal bandgap and temperature sensor ADC inputs: + * - \code + * adc_enable_internal_input(&adc_conf, ADC_INT_BANDGAP | ADC_INT_TEMPSENSE); + * \endcode + * -# Set ADC clock rate to maximum 200 KHz: + * - \code adc_set_clock_rate(&adc_conf, 200000UL); \endcode + * -# Write the configuration to the ADC: + * - \code adc_write_configuration(&MY_ADC, &adc_conf); \endcode + * -# Set up single-ended input from the internal temperature sensor, then write + * the config to the first channel (0): + * - \code + * adcch_set_input(&adcch_conf, ADCCH_POS_TEMPSENSE, ADCCH_NEG_NONE, 1); + * adcch_write_configuration(&MY_ADC, ADC_CH0, &adcch_conf); + * \endcode + * -# Set up single-ended input from the internal bandgap voltage, then write + * the config to the second channel (1): + * - \code + * adcch_set_input(&adcch_conf, ADCCH_POS_BANDGAP, ADCCH_NEG_NONE, 1); + * adcch_write_configuration(&MY_ADC, ADC_CH1, &adcch_conf); + * \endcode + * -# Initialize the clock system, event system, ADC trigger timer, and the ADC: + * - \code + * sysclk_init(); + * evsys_init(); + * tc_init(); + * adc_init(); + * \endcode + * + * \section adc_use_case_2_usage Usage steps + * + * \subsection adc_use_case_2_usage_code Example code + * + * Add to \c main(): + * \code + * adc_enable(&MY_ADC); + * + * do { + * uint16_t tmp_result; + * uint16_t bg_result; + * + * if (adc_get_interrupt_flag(&MY_ADC, ADC_CH0 | ADC_CH1) + * == (ADC_CH0 | ADC_CH1)) { + * tmp_result = adc_get_result(&MY_ADC, ADC_CH0); + * bg_result = adc_get_result(&MY_ADC, ADC_CH1); + * + * adc_clear_interrupt_flag(&MY_ADC, ADC_CH0 | ADC_CH1); + * } + * } while (true); + * \endcode + * + * \subsection adc_use_case_2_usage_flow Workflow + * + * -# Enable the configured ADC module, so that it will begin conversions when + * triggered: + * - \code adc_enable(&MY_ADC); \endcode + * -# Create an infinite loop so that conversions will be processed forever: + * - \code + * do { + * // ... + * } while (true); + * \endcode + * -# Within the loop, create local variables to contain the ADC result of each + * channel (internal temperature sensor and internal bandgap voltage): + * - \code + * int16_t temp_result; + * int16_t bg_result; + * \endcode + * -# Test if both ADC channel 0 and channel 1 have completed a conversion by + * testing the respective channel conversion complete interrupt flags: + * - \code + * if (adc_get_interrupt_flag(&MY_ADC, ADC_CH0 | ADC_CH1) + * == (ADC_CH0 | ADC_CH1)) { + * \endcode + * -# Store the channel result values into the local variables created earlier: + * - \code + * tmp_result = adc_get_result(&MY_ADC, ADC_CH0); + * bg_result = adc_get_result(&MY_ADC, ADC_CH1); + * \endcode + * -# Clear both ADC channel conversion complete interrupt flags, so that we can + * detect future conversions at a later stage: + * - \code adc_clear_interrupt_flag(&MY_ADC, ADC_CH0 | ADC_CH1); \endcode + */ + +#endif /* ADC_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/adc/xmega_aau/adc_aau.c b/bacnet-stack/ports/xplained/ASF/xmega/drivers/adc/xmega_aau/adc_aau.c new file mode 100644 index 00000000..9d20164e --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/adc/xmega_aau/adc_aau.c @@ -0,0 +1,370 @@ +/** + * \file + * + * \brief AVR XMEGA A/AU specific ADC driver implementation + * + * Copyright (C) 2012-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#include "../adc.h" + +/** + * \ingroup adc_module_group + * @{ + */ + +/** \name Internal functions for driver */ +/** @{ */ +extern void adc_enable_clock(ADC_t * adc); +extern void adc_disable_clock(ADC_t * adc); + +/** @} */ + +/** \name ADC interrupt callback function */ +/** @{ */ + +#ifdef ADCA +# ifdef CONFIG_ADC_CALLBACK_ENABLE + +extern adc_callback_t adca_callback; + +/** + * \internal + * \brief ISR for channel 0 on ADC A + * + * Calls the callback function that has been set for the ADC when the channel's + * interrupt flag is set, if its interrupt has been enabled. + */ +ISR(ADCA_CH0_vect) +{ + adca_callback(&ADCA, ADC_CH0, adc_get_result(&ADCA, ADC_CH0)); +} + +/** + * \internal + * \brief ISR for channel 1 on ADC A + * + * Calls the callback function that has been set for the ADC when the channel's + * interrupt flag is set, if its interrupt has been enabled. + */ +ISR(ADCA_CH1_vect) +{ + adca_callback(&ADCA, ADC_CH1, adc_get_result(&ADCA, ADC_CH1)); +} + +/** + * \internal + * \brief ISR for channel 2 on ADC A + * + * Calls the callback function that has been set for the ADC when the channel's + * interrupt flag is set, if its interrupt has been enabled. + */ +ISR(ADCA_CH2_vect) +{ + adca_callback(&ADCA, ADC_CH2, adc_get_result(&ADCA, ADC_CH2)); +} + +/** + * \internal + * \brief ISR for channel 3 on ADC A + * + * Calls the callback function that has been set for the ADC when the channel's + * interrupt flag is set, if its interrupt has been enabled. + */ +ISR(ADCA_CH3_vect) +{ + adca_callback(&ADCA, ADC_CH3, adc_get_result(&ADCA, ADC_CH3)); +} + +# endif +#endif + +#ifdef ADCB +# ifdef CONFIG_ADC_CALLBACK_ENABLE + +extern adc_callback_t adcb_callback; + +/** + * \internal + * \brief ISR for channel 0 on ADC B + * + * Calls the callback function that has been set for the ADC when the channel's + * interrupt flag is set, if its interrupt has been enabled. + */ +ISR(ADCB_CH0_vect) +{ + adcb_callback(&ADCB, ADC_CH0, adc_get_result(&ADCB, ADC_CH0)); +} + +/** + * \internal + * \brief ISR for channel 1 on ADC B + * + * Calls the callback function that has been set for the ADC when the channel's + * interrupt flag is set, if its interrupt has been enabled. + */ +ISR(ADCB_CH1_vect) +{ + adcb_callback(&ADCB, ADC_CH1, adc_get_result(&ADCB, ADC_CH1)); +} + +/** + * \internal + * \brief ISR for channel 2 on ADC B + * + * Calls the callback function that has been set for the ADC when the channel's + * interrupt flag is set, if its interrupt has been enabled. + */ +ISR(ADCB_CH2_vect) +{ + adcb_callback(&ADCB, ADC_CH2, adc_get_result(&ADCB, ADC_CH2)); +} + +/** + * \internal + * \brief ISR for channel 3 on ADC B + * + * Calls the callback function that has been set for the ADC when the channel's + * interrupt flag is set, if its interrupt has been enabled. + */ +ISR(ADCB_CH3_vect) +{ + adcb_callback(&ADCB, ADC_CH3, adc_get_result(&ADCB, ADC_CH3)); +} + +# endif +#endif + +/** @} */ + +/** \name ADC module configuration */ +/** @{ */ + +/** + * \brief Write configuration to ADC module + * + * Disables the ADC and flushes its pipeline before writing the specified + * configuration and factory calibration value to it. If the ADC was enabled + * upon entry of the function, it is enabled upon function return. + * + * \param adc Pointer to ADC module. + * \param conf Pointer to ADC module configuration. + */ +void adc_write_configuration(ADC_t * adc, + const struct adc_config *conf) +{ + uint16_t cal; + uint8_t enable; + irqflags_t flags; + +#ifdef ADCA + if ((uintptr_t) adc == (uintptr_t) & ADCA) { + cal = adc_get_calibration_data(ADC_CAL_ADCA); + } else +#endif + +#ifdef ADCB + if ((uintptr_t) adc == (uintptr_t) & ADCB) { + cal = adc_get_calibration_data(ADC_CAL_ADCB); + } else +#endif + + { + Assert(0); + return; + } + + flags = cpu_irq_save(); + adc_enable_clock(adc); + enable = adc->CTRLA & ADC_ENABLE_bm; + + adc->CTRLA = ADC_FLUSH_bm; + adc->CAL = cal; + adc->CMP = conf->cmp; + adc->REFCTRL = conf->refctrl; + adc->PRESCALER = conf->prescaler; + adc->EVCTRL = conf->evctrl; + adc->CTRLB = conf->ctrlb; + + adc->CTRLA = enable | conf->ctrla; + + adc_disable_clock(adc); + + cpu_irq_restore(flags); +} + +/** + * \brief Read configuration from ADC module + * + * Reads out the current configuration of the ADC module to the specified + * buffer. + * + * \param adc Pointer to ADC module. + * \param conf Pointer to ADC module configuration. + */ +void adc_read_configuration(ADC_t * adc, + struct adc_config *conf) +{ + irqflags_t flags = cpu_irq_save(); + + adc_enable_clock(adc); + + conf->ctrla = adc->CTRLA & ADC_DMASEL_gm; + + conf->cmp = adc->CMP; + conf->refctrl = adc->REFCTRL; + conf->prescaler = adc->PRESCALER; + conf->evctrl = adc->EVCTRL; + conf->ctrlb = adc->CTRLB; + + adc_disable_clock(adc); + + cpu_irq_restore(flags); +} + +/** @} */ + +/** @} */ + +/** + * \ingroup adc_channel_group + * @{ + */ + +/** \name ADC channel configuration */ +/** @{ */ + +/** + * \brief Write configuration to ADC channel + * + * Writes the specified configuration to the ADC channel. + * + * \param adc Pointer to ADC module. + * \param ch_mask Mask of ADC channel(s): + * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel + * can be given in mask) + * \param ch_conf Pointer to ADC channel configuration. + * + * \note The specified ADC's callback function must be set before this function + * is called if callbacks are enabled and interrupts are enabled in the + * channel configuration. + */ +void adcch_write_configuration(ADC_t * adc, + uint8_t ch_mask, + const struct adc_channel_config *ch_conf) +{ + ADC_CH_t *adc_ch; + irqflags_t flags; + + adc_ch = adc_get_channel(adc, ch_mask); + + flags = cpu_irq_save(); + +#if defined(CONFIG_ADC_CALLBACK_ENABLE) && defined(_ASSERT_ENABLE_) + if ((adc_ch->INTCTRL & ADC_CH_INTLVL_gm) != ADC_CH_INTLVL_OFF_gc) { +# ifdef ADCA + if ((uintptr_t) adc == (uintptr_t) & ADCA) { + Assert(adca_callback); + } else +# endif + +# ifdef ADCB + if ((uintptr_t) adc == (uintptr_t) & ADCB) { + Assert(adcb_callback); + } else +# endif + + { + Assert(0); + return; + } + } +#endif + + adc_enable_clock(adc); + adc_ch->CTRL = ch_conf->ctrl; + adc_ch->INTCTRL = ch_conf->intctrl; + adc_ch->MUXCTRL = ch_conf->muxctrl; + if (ch_mask & ADC_CH0) { + /* USB devices has channel scan available on ADC channel 0 */ + adc_ch->SCAN = ch_conf->scan; + } + adc_disable_clock(adc); + + cpu_irq_restore(flags); +} + +/** + * \brief Read configuration from ADC channel + * + * Reads out the current configuration from the ADC channel to the specified + * buffer. + * + * \param adc Pointer to ADC module. + * \param ch_mask Mask of ADC channel(s): + * \arg \c ADC_CHn , where \c n specifies the channel. (Only a single channel + * can be given in mask) + * \param ch_conf Pointer to ADC channel configuration. + */ +void adcch_read_configuration(ADC_t * adc, + uint8_t ch_mask, + struct adc_channel_config *ch_conf) +{ + ADC_CH_t *adc_ch; + irqflags_t flags; + + adc_ch = adc_get_channel(adc, ch_mask); + + flags = cpu_irq_save(); + + adc_enable_clock(adc); + ch_conf->ctrl = adc_ch->CTRL; + ch_conf->intctrl = adc_ch->INTCTRL; + ch_conf->muxctrl = adc_ch->MUXCTRL; + if (ch_mask & ADC_CH0) { + /* USB devices has channel scan available on ADC channel 0 */ + ch_conf->scan = adc_ch->SCAN; + } + adc_disable_clock(adc); + + cpu_irq_restore(flags); +} + +/** @} */ + +/** @} */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/cpu/ccp.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/cpu/ccp.h new file mode 100644 index 00000000..2fd2bc13 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/cpu/ccp.h @@ -0,0 +1,120 @@ +/** + * \file + * + * \brief Configuration Change Protection write functions + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef CPU_CCP_H +#define CPU_CCP_H +#include + +/** + * \defgroup ccp_group Configuration Change Protection + * + * See \ref xmega_ccp_quickstart. + * + * Function for writing to protected IO registers. + * @{ + */ + +#if defined(__DOXYGEN__) +//! \name IAR Memory Model defines. +//@{ + +/** + * \def CONFIG_MEMORY_MODEL_TINY + * \brief Configuration symbol to enable 8 bit pointers. + * + */ +# define CONFIG_MEMORY_MODEL_TINY + +/** + * \def CONFIG_MEMORY_MODEL_SMALL + * \brief Configuration symbol to enable 16 bit pointers. + * \note If no memory model is defined, SMALL is default. + * + */ +# define CONFIG_MEMORY_MODEL_SMALL + + +/** + * \def CONFIG_MEMORY_MODEL_LARGE + * \brief Configuration symbol to enable 24 bit pointers. + * + */ +# define CONFIG_MEMORY_MODEL_LARGE + +//@} +#endif + + +/** + * \brief Write to a CCP-protected 8-bit I/O register + * + * \param addr Address of the I/O register + * \param value Value to be written + * + * \note Using IAR Embedded workbench, the choice of memory model has an impact + * on calling convention. The memory model is not visible to the + * preprocessor, so it must be defined in the Assembler preprocessor directives. + */ +extern void ccp_write_io (void *addr, uint8_t value); + +/** @} */ + +/** + * \page xmega_ccp_quickstart Quick start guide for CCP driver + * + * This is the quick start guide for the \ref ccp_group + * "Configuration Change Protection (CCP) driver", with step-by-step + * instructions on how to use the driver. + * + * The use case contains a code fragment, and this can be copied into, e.g., + * the main application function. + * + * \section ccp_basic_use_case Basic use case + * In this use case, the CCP is used to write to the protected XMEGA Clock + * Control register. + * + * \subsection ccp_basic_use_case_setup_flow Workflow + * -# call CCP write io to change system clock selection: + * - \code ccp_write_io((uint8_t *)&CLK.CTRL, CLK_SCLKSEL_RC32M_gc); \endcode + */ + +#endif /* CPU_CCP_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/cpu/ccp.s b/bacnet-stack/ports/xplained/ASF/xmega/drivers/cpu/ccp.s new file mode 100644 index 00000000..3f7e2797 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/cpu/ccp.s @@ -0,0 +1,100 @@ +/** + * \file + * + * \brief Configuration Change Protection + * + * Copyright (c) 2009 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include + +//! Value to write to CCP for access to protected IO registers. +#define CCP_IOREG 0xd8 + + /* + * GNU and IAR use different calling conventions. Since this is + * a very small and simple function to begin with, it's easier + * to implement it twice than to deal with the differences + * within a single implementation. + * + * Interrupts are disabled by hardware during the timed + * sequence, so there's no need to save/restore interrupt state. + */ + + PUBLIC_FUNCTION(ccp_write_io) + +#if defined(__GNUC__) + + out RAMPZ, r1 // Reset bits 23:16 of Z + movw r30, r24 // Load addr into Z + ldi r18, CCP_IOREG // Load magic CCP value + out CCP, r18 // Start CCP handshake + st Z, r22 // Write value to I/O register + ret // Return to caller + +#elif defined(__IAR_SYSTEMS_ASM__) + +# if !defined(CONFIG_MEMORY_MODEL_TINY) && !defined(CONFIG_MEMORY_MODEL_SMALL) \ + && !defined(CONFIG_MEMORY_MODEL_LARGE) +# define CONFIG_MEMORY_MODEL_SMALL +# endif + ldi r20, 0 + out RAMPZ, r20 // Reset bits 23:16 of Z +# if defined(CONFIG_MEMORY_MODEL_TINY) + mov r31, r20 // Reset bits 8:15 of Z + mov r30, r16 // Load addr into Z +# else + movw r30, r16 // Load addr into Z +# endif + ldi r21, CCP_IOREG // Load magic CCP value + out CCP, r21 // Start CCP handshake +# if defined(CONFIG_MEMORY_MODEL_TINY) + st Z, r17 // Write value to I/O register +# elif defined(CONFIG_MEMORY_MODEL_SMALL) + st Z, r18 // Write value to I/O register +# elif defined(CONFIG_MEMORY_MODEL_LARGE) + st Z, r19 // Write value to I/O register +# else +# error Unknown memory model in use, no idea how registers should be accessed +# endif + ret +#else +# error Unknown assembler +#endif + + END_FUNC(ccp_write_io) + END_FILE() diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/cpu/xmega_reset_cause.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/cpu/xmega_reset_cause.h new file mode 100644 index 00000000..65edc800 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/cpu/xmega_reset_cause.h @@ -0,0 +1,109 @@ +/** + * \file + * + * \brief Chip-specific reset cause functions + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef XMEGA_DRIVERS_CPU_RESET_CAUSE_H +#define XMEGA_DRIVERS_CPU_RESET_CAUSE_H + +#include "compiler.h" +#include "ccp.h" + +/** + * \ingroup reset_cause_group + * \defgroup xmega_reset_cause_group XMEGA reset cause + * + * See \ref reset_cause_quickstart + * + * @{ + */ + +/** + * \brief Chip-specific reset cause type capable of holding all chip reset + * causes. Typically reflects the size of the reset cause register. + */ +typedef uint8_t reset_cause_t; + +//! \internal \name Chip-specific reset causes +//@{ +//! \internal External reset cause +#define CHIP_RESET_CAUSE_EXTRST RST_EXTRF_bm +//! \internal brown-out detected reset cause, same as for CPU +#define CHIP_RESET_CAUSE_BOD_IO RST_BORF_bm +//! \internal Brown-out detected reset cause, same as for I/O +#define CHIP_RESET_CAUSE_BOD_CPU RST_BORF_bm +//! \internal On-chip debug system reset cause +#define CHIP_RESET_CAUSE_OCD RST_PDIRF_bm +//! \internal Power-on-reset reset cause +#define CHIP_RESET_CAUSE_POR RST_PORF_bm +//! \internal Software reset reset cause +#define CHIP_RESET_CAUSE_SOFT RST_SRF_bm +//! \internal Spike detected reset cause +#define CHIP_RESET_CAUSE_SPIKE RST_SDRF_bm +//! \internal Watchdog timeout reset cause +#define CHIP_RESET_CAUSE_WDT RST_WDRF_bm +//@} + +static inline reset_cause_t +reset_cause_get_causes (void) +{ + return (reset_cause_t) RST.STATUS; +} + +static inline void +reset_cause_clear_causes (reset_cause_t causes) +{ + RST.STATUS = causes; +} + +static inline void +reset_do_soft_reset (void) +{ + ccp_write_io ((void *) &RST.CTRL, RST_SWRST_bm); + + while (1) + { + /* Intentionally empty. */ + } +} + +//! @} + +#endif /* XMEGA_DRIVERS_CPU_RESET_CAUSE_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/nvm/nvm.c b/bacnet-stack/ports/xplained/ASF/xmega/drivers/nvm/nvm.c new file mode 100644 index 00000000..d4f1e42a --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/nvm/nvm.c @@ -0,0 +1,732 @@ +/** + * \file + * + * \brief Non Volatile Memory controller driver + * + * Copyright (C) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include "compiler.h" +#include "ccp.h" +#include "nvm.h" +#include + +/** + * \weakgroup nvm_signature_group + * @{ + */ + +/** + * \brief Read the device serial + * + * This function returns the device serial stored in the device. + * + * \note This function is modifying the NVM.CMD register. + * If the application are using program space access in interrupts + * (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts + * needs to be disabled when running EEPROM access functions. If not + * the program space reads will be corrupted. + * + * \retval storage Pointer to the structure where to store the device serial + */ +void nvm_read_device_serial(struct nvm_device_serial *storage) +{ + storage->lotnum0 = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(LOTNUM0)); + storage->lotnum1 = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(LOTNUM1)); + storage->lotnum2 = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(LOTNUM2)); + storage->lotnum3 = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(LOTNUM3)); + storage->lotnum4 = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(LOTNUM4)); + storage->lotnum5 = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(LOTNUM5)); + + storage->wafnum = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(WAFNUM)); + + storage->coordx0 = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(COORDX0)); + storage->coordx1 = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(COORDX1)); + storage->coordy0 = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(COORDY0)); + storage->coordy1 = + nvm_read_production_signature_row + (nvm_get_production_signature_row_offset(COORDY1)); +} + +//! @} + +/** + * \weakgroup nvm_eeprom_group + * @{ + */ + +/** + * \brief Read one byte from EEPROM using mapped access. + * + * This function reads one byte from EEPROM using mapped access. + * + * \param addr EEPROM address, between 0 and EEPROM_SIZE + * + * \return Byte value read from EEPROM. + */ +uint8_t nvm_eeprom_read_byte(eeprom_addr_t addr) +{ + uint8_t data; + Assert(addr <= EEPROM_SIZE); + + /* Wait until NVM is ready */ + nvm_wait_until_ready(); + eeprom_enable_mapping(); + data = *(uint8_t *) (addr + MAPPED_EEPROM_START), eeprom_disable_mapping(); + return data; +} + +/** + * \brief Read buffer within the eeprom + * + * \param address the address to where to read + * \param buf pointer to the data + * \param len the number of bytes to read + */ +void nvm_eeprom_read_buffer(eeprom_addr_t address, + void *buf, + uint16_t len) +{ + nvm_wait_until_ready(); + eeprom_enable_mapping(); + memcpy(buf, (void *) (address + MAPPED_EEPROM_START), len); + eeprom_disable_mapping(); +} + + +/** + * \brief Write one byte to EEPROM using IO mapping. + * + * This function writes one byte to EEPROM using IO-mapped access. + * This function will cancel all ongoing EEPROM page buffer loading + * operations, if any. + * + * \param address EEPROM address (max EEPROM_SIZE) + * \param value Byte value to write to EEPROM. + */ +void nvm_eeprom_write_byte(eeprom_addr_t address, + uint8_t value) +{ + uint8_t old_cmd; + + Assert(address <= EEPROM_SIZE); + /* Flush buffer to make sure no unintentional data is written and load + * the "Page Load" command into the command register. + */ + old_cmd = NVM.CMD; + nvm_eeprom_flush_buffer(); + // Wait until NVM is ready + nvm_wait_until_ready(); + nvm_eeprom_load_byte_to_buffer(address, value); + + // Set address to write to + NVM.ADDR2 = 0x00; + NVM.ADDR1 = (address >> 8) & 0xFF; + NVM.ADDR0 = address & 0xFF; + + /* Issue EEPROM Atomic Write (Erase&Write) command. Load command, write + * the protection signature and execute command. + */ + NVM.CMD = NVM_CMD_ERASE_WRITE_EEPROM_PAGE_gc; + nvm_exec(); + NVM.CMD = old_cmd; +} + +/** + * \brief Write buffer within the eeprom + * + * \param address the address to where to write + * \param buf pointer to the data + * \param len the number of bytes to write + */ +void nvm_eeprom_erase_and_write_buffer(eeprom_addr_t address, + const void *buf, + uint16_t len) +{ + while (len) { + if (((address % EEPROM_PAGE_SIZE) == 0) && (len >= EEPROM_PAGE_SIZE)) { + // A full page can be written + nvm_eeprom_load_page_to_buffer((uint8_t *) buf); + nvm_eeprom_atomic_write_page(address / EEPROM_PAGE_SIZE); + address += EEPROM_PAGE_SIZE; + buf = (uint8_t *) buf + EEPROM_PAGE_SIZE; + len -= EEPROM_PAGE_SIZE; + } else { + nvm_eeprom_write_byte(address++, *(uint8_t *) buf); + buf = (uint8_t *) buf + 1; + len--; + } + } +} + + +/** + * \brief Flush temporary EEPROM page buffer. + * + * This function flushes the EEPROM page buffers. This function will cancel + * any ongoing EEPROM page buffer loading operations, if any. + * This function also works for memory mapped EEPROM access. + * + * \note An EEPROM write operations will automatically flush the buffer for you. + * \note The function does not preserve the value of the NVM.CMD register + */ +void nvm_eeprom_flush_buffer(void) +{ + // Wait until NVM is ready + nvm_wait_until_ready(); + + // Flush EEPROM page buffer if necessary + if ((NVM.STATUS & NVM_EELOAD_bm) != 0) { + NVM.CMD = NVM_CMD_ERASE_EEPROM_BUFFER_gc; + nvm_exec(); + } +} + +/** + * \brief Load single byte into temporary page buffer. + * + * This function loads one byte into the temporary EEPROM page buffers. + * If memory mapped EEPROM is enabled, this function will not work. + * Make sure that the buffer is flushed before starting to load bytes. + * Also, if multiple bytes are loaded into the same location, they will + * be ANDed together, thus 0x55 and 0xAA will result in 0x00 in the buffer. + * + * \note Only one page buffer exist, thus only one page can be loaded with + * data and programmed into one page. If data needs to be written to + * different pages, the loading and writing needs to be repeated. + * + * \param byte_addr EEPROM Byte address, between 0 and EEPROM_PAGE_SIZE. + * \param value Byte value to write to buffer. + */ +void nvm_eeprom_load_byte_to_buffer(uint8_t byte_addr, + uint8_t value) +{ + // Wait until NVM is ready + nvm_wait_until_ready(); + + eeprom_enable_mapping(); + *(uint8_t *) (byte_addr + MAPPED_EEPROM_START) = value; + eeprom_disable_mapping(); +} + + +/** + * \brief Load entire page into temporary EEPROM page buffer. + * + * This function loads an entire EEPROM page from an SRAM buffer to + * the EEPROM page buffers. If memory mapped EEPROM is enabled, this + * function will not work. Make sure that the buffer is flushed before + * starting to load bytes. + * + * \note Only the lower part of the address is used to address the buffer. + * Therefore, no address parameter is needed. In the end, the data + * is written to the EEPROM page given by the address parameter to the + * EEPROM write page operation. + * + * \param values Pointer to SRAM buffer containing an entire page. + */ +void nvm_eeprom_load_page_to_buffer(const uint8_t * values) +{ + // Wait until NVM is ready + nvm_wait_until_ready(); + + // Load multiple bytes into page buffer + uint8_t i; + for (i = 0; i < EEPROM_PAGE_SIZE; ++i) { + nvm_eeprom_load_byte_to_buffer(i, *values); + ++values; + } +} + +/** + * \brief Erase and write bytes from page buffer into EEPROM. + * + * This function writes the contents of an already loaded EEPROM page + * buffer into EEPROM memory. + * + * As this is an atomic write, the page in EEPROM will be erased + * automatically before writing. Note that only the page buffer locations + * that have been loaded will be used when writing to EEPROM. Page buffer + * locations that have not been loaded will be left untouched in EEPROM. + * + * \param page_addr EEPROM Page address, between 0 and EEPROM_SIZE/EEPROM_PAGE_SIZE + */ +void nvm_eeprom_atomic_write_page(uint8_t page_addr) +{ + // Wait until NVM is ready + nvm_wait_until_ready(); + + // Calculate page address + uint16_t address = (uint16_t) (page_addr * EEPROM_PAGE_SIZE); + + Assert(address <= EEPROM_SIZE); + + // Set address + NVM.ADDR2 = 0x00; + NVM.ADDR1 = (address >> 8) & 0xFF; + NVM.ADDR0 = address & 0xFF; + + // Issue EEPROM Atomic Write (Erase&Write) command + nvm_issue_command(NVM_CMD_ERASE_WRITE_EEPROM_PAGE_gc); +} + +/** + * \brief Write (without erasing) EEPROM page. + * + * This function writes the contents of an already loaded EEPROM page + * buffer into EEPROM memory. + * + * As this is a split write, the page in EEPROM will _not_ be erased + * before writing. + * + * \param page_addr EEPROM Page address, between 0 and EEPROM_SIZE/EEPROM_PAGE_SIZE + */ +void nvm_eeprom_split_write_page(uint8_t page_addr) +{ + // Wait until NVM is ready + nvm_wait_until_ready(); + + // Calculate page address + uint16_t address = (uint16_t) (page_addr * EEPROM_PAGE_SIZE); + + Assert(address <= EEPROM_SIZE); + + // Set address + NVM.ADDR2 = 0x00; + NVM.ADDR1 = (address >> 8) & 0xFF; + NVM.ADDR0 = address & 0xFF; + + // Issue EEPROM Split Write command + nvm_issue_command(NVM_CMD_WRITE_EEPROM_PAGE_gc); +} + +/** + * \brief Fill temporary EEPROM page buffer with value. + * + * This fills the the EEPROM page buffers with a given value. + * If memory mapped EEPROM is enabled, this function will not work. + * + * \note Only the lower part of the address is used to address the buffer. + * Therefore, no address parameter is needed. In the end, the data + * is written to the EEPROM page given by the address parameter to the + * EEPROM write page operation. + * + * \param value Value to copy to the page buffer. + */ +void nvm_eeprom_fill_buffer_with_value(uint8_t value) +{ + nvm_eeprom_flush_buffer(); + // Wait until NVM is ready + nvm_wait_until_ready(); + // Load multiple bytes into page buffer + uint8_t i; + for (i = 0; i < EEPROM_PAGE_SIZE; ++i) { + nvm_eeprom_load_byte_to_buffer(i, value); + } +} + +/** + * \brief Erase bytes from EEPROM page. + * + * This function erases bytes from one EEPROM page, so that every location + * written to in the page buffer reads 0xFF. + * + * \param page_addr EEPROM Page address, between 0 and EEPROM_SIZE/EEPROM_PAGE_SIZE + */ +void nvm_eeprom_erase_bytes_in_page(uint8_t page_addr) +{ + // Wait until NVM is ready + nvm_wait_until_ready(); + + // Calculate page address + uint16_t address = (uint16_t) (page_addr * EEPROM_PAGE_SIZE); + + Assert(address <= EEPROM_SIZE); + + // Set address + NVM.ADDR2 = 0x00; + NVM.ADDR1 = (address >> 8) & 0xFF; + NVM.ADDR0 = address & 0xFF; + + // Issue EEPROM Erase command + nvm_issue_command(NVM_CMD_ERASE_EEPROM_PAGE_gc); +} + +/** + * \brief Erase EEPROM page. + * + * This function erases one EEPROM page, so that every location reads 0xFF. + * + * \param page_addr EEPROM Page address, between 0 and EEPROM_SIZE/EEPROM_PAGE_SIZE + */ +void nvm_eeprom_erase_page(uint8_t page_addr) +{ + // Mark all addresses to be deleted + nvm_eeprom_fill_buffer_with_value(0xff); + // Erase bytes + nvm_eeprom_erase_bytes_in_page(page_addr); +} + + +/** + * \brief Erase bytes from all EEPROM pages. + * + * This function erases bytes from all EEPROM pages, so that every location + * written to in the page buffer reads 0xFF. + */ +void nvm_eeprom_erase_bytes_in_all_pages(void) +{ + // Wait until NVM is ready + nvm_wait_until_ready(); + + // Issue EEPROM Erase All command + nvm_issue_command(NVM_CMD_ERASE_EEPROM_gc); +} + +/** + * \brief Erase entire EEPROM memory. + * + * This function erases the entire EEPROM memory block to 0xFF. + */ +void nvm_eeprom_erase_all(void) +{ + // Mark all addresses to be deleted + nvm_eeprom_fill_buffer_with_value(0xff); + // Erase all pages + nvm_eeprom_erase_bytes_in_all_pages(); +} + +//! @} + + +//! @} + + +/** + * \weakgroup nvm_flash_group + * @{ + */ + +/** + * \brief Issue flash range CRC command + * + * This function sets the FLASH range CRC command in the NVM.CMD register. + * It then loads the start and end byte address of the part of FLASH to + * generate a CRC-32 for into the ADDR and DATA registers and finally performs + * the execute command. + * + * \note Should only be called from the CRC module. The function saves and + * restores the NVM.CMD register, but if this + * function is called from an interrupt, interrupts must be disabled + * before this function is called. + * + * \param start_addr end byte address + * \param end_addr start byte address + */ +void nvm_issue_flash_range_crc(flash_addr_t start_addr, + flash_addr_t end_addr) +{ + uint8_t old_cmd; + // Save current nvm command + old_cmd = NVM.CMD; + + // Load the NVM CMD register with the Flash Range CRC command + NVM.CMD = NVM_CMD_FLASH_RANGE_CRC_gc; + + // Load the start byte address in the NVM Address Register + NVM.ADDR0 = start_addr & 0xFF; + NVM.ADDR1 = (start_addr >> 8) & 0xFF; +#if (FLASH_SIZE >= 0x10000UL) + NVM.ADDR2 = (start_addr >> 16) & 0xFF; +#endif + + // Load the end byte address in NVM Data Register + NVM.DATA0 = end_addr & 0xFF; + NVM.DATA1 = (end_addr >> 8) & 0xFF; +#if (FLASH_SIZE >= 0x10000UL) + NVM.DATA2 = (end_addr >> 16) & 0xFF; +#endif + + // Execute command + ccp_write_io((uint8_t *) & NVM.CTRLA, NVM_CMDEX_bm); + + // Restore command register + NVM.CMD = old_cmd; +} + +/** + * \brief Read buffer within the application section + * + * \param address the address to where to read + * \param buf pointer to the data + * \param len the number of bytes to read + */ +void nvm_flash_read_buffer(flash_addr_t address, + void *buf, + uint16_t len) +{ +#if (FLASH_SIZE>0x10000) + uint32_t opt_address = address; +#else + uint16_t opt_address = (uint16_t) address; +#endif + nvm_wait_until_ready(); + while (len) { + *(uint8_t *) buf = nvm_flash_read_byte(opt_address); + buf = (uint8_t *) buf + 1; + opt_address++; + len--; + } +} + +/** + * \brief Read buffer within the user section + * + * \param address the address to where to read + * \param buf pointer to the data + * \param len the number of bytes to read + */ +void nvm_user_sig_read_buffer(flash_addr_t address, + void *buf, + uint16_t len) +{ + uint16_t opt_address = (uint16_t) address & (FLASH_PAGE_SIZE - 1); + while (len) { + *(uint8_t *) buf = nvm_read_user_signature_row(opt_address); + buf = (uint8_t *) buf + 1; + opt_address++; + len--; + } +} + +/** + * \brief Write specific parts of user flash section + * + * \param address the address to where to write + * \param buf pointer to the data + * \param len the number of bytes to write + * \param b_blank_check if True then the page flash is checked before write + * to run or not the erase page command. + * + * Set b_blank_check to false if all application flash is erased before. + */ +void nvm_user_sig_write_buffer(flash_addr_t address, + const void *buf, + uint16_t len, + bool b_blank_check) +{ + uint16_t w_value; + uint16_t page_pos; + uint16_t opt_address = (uint16_t) address; + bool b_flag_erase = false; + + while (len) { + for (page_pos = 0; page_pos < FLASH_PAGE_SIZE; page_pos += 2) { + if (b_blank_check) { + // Read flash to know if the erase command is mandatory + LSB(w_value) = nvm_read_user_signature_row(page_pos); + MSB(w_value) = nvm_read_user_signature_row(page_pos + 1); + if (w_value != 0xFFFF) { + b_flag_erase = true; // The page is not empty + } + } else { + w_value = 0xFFFF; + } + // Update flash buffer + if (len) { + if (opt_address == page_pos) { + // The MSB of flash word must be changed + // because the address is even + len--; + opt_address++; + LSB(w_value) = *(uint8_t *) buf; + buf = (uint8_t *) buf + 1; + } + } + if (len) { + if (opt_address == (page_pos + 1)) { + // The LSB of flash word must be changed + // because the user buffer is not empty + len--; + opt_address++; + MSB(w_value) = *(uint8_t *) buf; + buf = (uint8_t *) buf + 1; + } + } + // Load flash buffer + nvm_flash_load_word_to_buffer(page_pos, w_value); + } + } + // Write flash buffer + if (b_flag_erase) { + nvm_flash_erase_user_section(); + } + nvm_flash_write_user_page(); +} + +/** + * \brief Erase and write specific parts of application flash section + * + * \param address the address to where to write + * \param buf pointer to the data + * \param len the number of bytes to write + * \param b_blank_check if True then the page flash is checked before write + * to run or not the erase page command. + * + * Set b_blank_check to false if all application flash is erased before. + */ +void nvm_flash_erase_and_write_buffer(flash_addr_t address, + const void *buf, + uint16_t len, + bool b_blank_check) +{ + uint16_t w_value; + uint16_t page_pos; + bool b_flag_erase; +#if (FLASH_SIZE>0x10000) + uint32_t page_address; + uint32_t opt_address = address; +#else + uint16_t page_address; + uint16_t opt_address = (uint16_t) address; +#endif + + // Compute the start of the page to be modified + page_address = opt_address - (opt_address % FLASH_PAGE_SIZE); + + // For each page + while (len) { + b_flag_erase = false; + + nvm_wait_until_ready(); + for (page_pos = 0; page_pos < FLASH_PAGE_SIZE; page_pos += 2) { + if (b_blank_check) { + // Read flash to know if the erase command is mandatory + w_value = nvm_flash_read_word(page_address); + if (w_value != 0xFFFF) { + b_flag_erase = true; // The page is not empty + } + } else { + w_value = 0xFFFF; + } + + // Update flash buffer + if (len) { + if (opt_address == page_address) { + // The MSB of flash word must be changed + // because the address is even + len--; + opt_address++; + LSB(w_value) = *(uint8_t *) buf; + buf = (uint8_t *) buf + 1; + } + } + if (len) { + if (opt_address == (page_address + 1)) { + // The LSB of flash word must be changed + // because the user buffer is not empty + len--; + opt_address++; + MSB(w_value) = *(uint8_t *) buf; + buf = (uint8_t *) buf + 1; + } + } + // Load flash buffer + nvm_flash_load_word_to_buffer(page_address, w_value); + page_address += 2; + } + + // Write flash buffer + if (b_flag_erase) { + nvm_flash_atomic_write_app_page(page_address - FLASH_PAGE_SIZE); + } else { + nvm_flash_split_write_app_page(page_address - FLASH_PAGE_SIZE); + } + } +} + +//! @} + +/** + * \weakgroup nvm_fuse_lock_group + * @{ + */ + +/** + * \brief Read a fuse byte. + * + * This function reads and returns the value of a given fuse byte. + * + * \param fuse Fuse byte to read. + * + * \return Byte value of fuse. + */ +uint8_t nvm_fuses_read(enum fuse_byte_t fuse) +{ + // Wait until NVM is ready + nvm_wait_until_ready(); + + // Set address + NVM.ADDR0 = fuse; + + // Issue READ_FUSES command + nvm_issue_command(NVM_CMD_READ_FUSES_gc); + + return NVM.DATA0; +} + +//! @} diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/nvm/nvm.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/nvm/nvm.h new file mode 100644 index 00000000..0204a283 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/nvm/nvm.h @@ -0,0 +1,1093 @@ +/** + * \file + * + * \brief Non Volatile Memory controller driver + * + * Copyright (c) 2010-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef NVM_H +#define NVM_H + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * \defgroup nvm_group NVM driver + * + * See \ref xmega_nvm_quickstart + * + * \brief Low-level driver implementation for the AVR XMEGA Non Volatile + * Memory Controller (NVM). + * + * The XMEGA NVM controller interfaces the internal non-volatile memories + * in the XMEGA devices. Program memory, EEPROM and signature row is can be + * interfaced by the module. See the documentation of each sub-module for + * more information. + * + * \note If using GCC and the flash sub-module, remember to configure + * the boot section in the make file. More information in the sub-module + * documentation. + * + * \section xmega_nvm_quickstart_section Quick Start Guide + * See \ref xmega_nvm_quickstart + */ + +/** + * \defgroup nvm_generic_group NVM driver generic module handling + * \ingroup nvm_group + * \brief Support functions for the NVM driver. + * + * These functions are helper functions for the functions of the + * \ref nvm_group "NVM driver". + * + * @{ + */ + +/** + * \brief Wait for any NVM access to finish. + * + * This function is blocking and waits for any NVM access to finish. + * Use this function before any NVM accesses, if you are not certain that + * any previous operations are finished yet. + */ + static inline void nvm_wait_until_ready (void) + { + do + { + // Block execution while waiting for the NVM to be ready + } + while ((NVM.STATUS & NVM_NVMBUSY_bm) == NVM_NVMBUSY_bm); + } + +/** + * \brief Non-Volatile Memory Execute Command + * + * This function sets the CCP register before setting the CMDEX bit in the + * NVM.CTRLA register. + * + * \note The correct NVM command must be set in the NVM.CMD register before + * calling this function. + */ + static inline void nvm_exec (void) + { + ccp_write_io ((uint8_t *) & NVM.CTRLA, NVM_CMDEX_bm); + } + +/** + * \brief Non-Volatile Memory Execute Specific Command + * + * This function sets a command in the NVM.CMD register, then performs an + * execute command by writing the CMDEX bit to the NVM.CTRLA register. + * + * \note The function saves and restores the NVM.CMD register, but if this + * function is called from an interrupt, interrupts must be disabled + * before this function is called. + * + * \param nvm_command NVM Command to execute. + */ + static inline void nvm_issue_command (NVM_CMD_t nvm_command) + { + uint8_t old_cmd; + + old_cmd = NVM.CMD; + NVM.CMD = nvm_command; + ccp_write_io ((uint8_t *) & NVM.CTRLA, NVM_CMDEX_bm); + NVM.CMD = old_cmd; + } + +/** + * \brief Read one byte using the LDI instruction + * \internal + * + * This function sets the specified NVM_CMD, reads one byte using at the + * specified byte address with the LPM instruction. NVM_CMD is restored after + * use. + * + * \note Interrupts should be disabled before running this function + * if program memory/NVM controller is accessed from ISRs. + * + * \param nvm_cmd NVM command to load before running LPM + * \param address Byte offset into the signature row + */ + uint8_t nvm_read_byte (uint8_t nvm_cmd, uint16_t address); + + +/** + * \brief Perform SPM write + * \internal + * + * This function sets the specified NVM_CMD, sets CCP and then runs the SPM + * instruction to write to flash. + * + * \note Interrupts should be disabled before running this function + * if program memory/NVM controller is accessed from ISRs. + * + * \param addr Address to perform the SPM on. + * \param nvm_cmd NVM command to use in the NVM_CMD register + */ + void nvm_common_spm (uint32_t addr, uint8_t nvm_cmd); + +//! @} + +/** + * \defgroup nvm_signature_group NVM driver signature handling + * \ingroup nvm_group + * \brief Handling of signature rows + * + * Functions for handling signature rows. The following is supported: + * - Reading values from production and user signature row + * - Reading device id + * - Reading device revision + * - Reading device serial + * + * \note Some of these functions are modifying the NVM.CMD register. + * If the application are using program space access in interrupts + * (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts + * needs to be disabled when running EEPROM access functions. If not, + * the program space reads will be corrupted. See documentation for + * each individual function. + * \note Do not use the functions of this module in an interrupt service + * routine (ISR), since the functions can take several milliseconds to + * complete and hence block the interrupt for several milliseconds. + * In addition the functions of this module are modifying the page buffer + * which will corrupt any ongoing EEPROM handing used outside an ISR. + * @{ + */ + +/** + * \brief Structure containing the device ID + * + * This structure can be used to store the device ID of a device. + */ + struct nvm_device_id + { + union + { + struct + { + uint8_t devid0; + uint8_t devid1; + uint8_t devid2; + }; + uint8_t byte[3]; + }; + }; + +/** + * \brief Structure containing the device serial + * + * This structure can be used to store the serial number of a device. + */ + struct nvm_device_serial + { + union + { + struct + { + uint8_t lotnum0; + uint8_t lotnum1; + uint8_t lotnum2; + uint8_t lotnum3; + uint8_t lotnum4; + uint8_t lotnum5; + uint8_t wafnum; + uint8_t coordx0; + uint8_t coordx1; + uint8_t coordy0; + uint8_t coordy1; + }; + uint8_t byte[11]; + }; + }; + +/** + * \brief Get offset of calibration bytes in the production signature row + * + * \param regname Name of register within the production signature row + * \retval Offset of register into the production signature row + */ +#if defined(__GNUC__) +# define nvm_get_production_signature_row_offset(regname) \ + offsetof(NVM_PROD_SIGNATURES_t, regname) +#elif defined(__ICCAVR__) +# define nvm_get_production_signature_row_offset(regname) (regname##_offset) +#else +# error Unknown compiler +#endif + + +/** + * \brief Read one byte from the production signature row + * + * This function reads one byte from the production signature row of the device + * at the given address. + * + * \note This function is modifying the NVM.CMD register. + * If the application are using program space access in interrupts + * (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts + * needs to be disabled when running EEPROM access functions. If not + * the program space reads will be corrupted. + * + * \param address Byte offset into the signature row + */ + static inline uint8_t nvm_read_production_signature_row (uint8_t address) + { + return nvm_read_byte (NVM_CMD_READ_CALIB_ROW_gc, address); + } + +/** + * \brief Read one byte from the user signature row + * + * This function reads one byte from the user signature row of the device + * at the given address. + * + * \note This function is modifying the NVM.CMD register. + * If the application are using program space access in interrupts + * (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts + * needs to be disabled when running EEPROM access functions. If not + * the program space reads will be corrupted. + * + * \param address Byte offset into the signature row + */ + static inline uint8_t nvm_read_user_signature_row (uint16_t address) + { + return nvm_read_byte (NVM_CMD_READ_USER_SIG_ROW_gc, address); + } + +/** + * \brief Read the device id + * + * This function returns the device ID stored in the device. + * + * \retval storage Pointer to the structure where to store the device id + */ + static inline void nvm_read_device_id (struct nvm_device_id *storage) + { + storage->devid0 = MCU.DEVID0; + storage->devid1 = MCU.DEVID1; + storage->devid2 = MCU.DEVID2; + } + +/** + * \brief Read the device revision + * + * This function returns the device revision stored in the device. + * + * \retval unsigned 8 bit value with the current device revision. + */ + static inline uint8_t nvm_read_device_rev (void) + { + return MCU.REVID; + } + + void nvm_read_device_serial (struct nvm_device_serial *storage); + +//! @} + + +/** + * \defgroup nvm_eeprom_group NVM driver EEPROM handling + * \ingroup nvm_group + * \brief Functions for handling internal EEPROM memory. + * + * The internal EEPROM can be used to store data that will persist after + * power is removed. This can typically be used to store calibration data, + * application state, encryption keys or other data that need to be preserved + * when power is removed. + * + * The functions in this module uses IO register access to manipulate the + * EEPROM. + * + * \note The functions in this module are modifying the NVM.CMD register. + * If the application are using program space access in interrupts + * (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts + * needs to be disabled when running EEPROM access functions. If not + * the program space reads will be corrupted. + * @{ + */ + +#ifndef EEPROM_PAGE_SIZE +# if XMEGA_A || XMEGA_AU || XMEGA_B || XMEGA_C || XMEGA_D || XMEGA_E +# define EEPROM_PAGE_SIZE 32 +# else +# error Unknown EEPROM page size +# endif +#endif + +#ifndef CONFIG_NVM_IGNORE_XMEGA_A3_D3_REVB_ERRATA +# if XMEGA_A3 || XMEGA_D3 +# error This NVM driver does not support rev B of XMEGA A3/D3 devices. \ + Set CONFIG_NVM_IGNORE_XMEGA_A3_D3_REVB_ERRATA to disable this message +# endif +#endif + +/** + * Data type for holding eeprom memory addresses. + */ + typedef uint16_t eeprom_addr_t; + + +/*! \brief Enable EEPROM mapping into data space. + * + * This macro enables mapping of EEPROM into data space. + * EEPROM starts at EEPROM_START in data memory. Read access + * can be done similar to ordinary SRAM access. + * + * \note This disables IO-mapped access to EEPROM, although page erase and + * write operations still needs to be done through IO register. + */ + static inline void eeprom_enable_mapping (void) + { +#if !XMEGA_E + NVM_CTRLB = NVM_CTRLB | NVM_EEMAPEN_bm; +#endif + } + + +/*! \brief Disable EEPROM mapping into data space. + * + * This macro disables mapping of EEPROM into data space. + * IO mapped access is now enabled. + */ + static inline void eeprom_disable_mapping (void) + { +#if !XMEGA_E + NVM_CTRLB = NVM_CTRLB & ~NVM_EEMAPEN_bm; +#endif + } + + + uint8_t nvm_eeprom_read_byte (eeprom_addr_t addr); + void nvm_eeprom_write_byte (eeprom_addr_t address, uint8_t value); + void nvm_eeprom_read_buffer (eeprom_addr_t address, void *buf, + uint16_t len); + void nvm_eeprom_erase_and_write_buffer (eeprom_addr_t address, + const void *buf, uint16_t len); + + void nvm_eeprom_flush_buffer (void); + void nvm_eeprom_load_byte_to_buffer (uint8_t byte_addr, uint8_t value); + void nvm_eeprom_load_page_to_buffer (const uint8_t * values); + void nvm_eeprom_atomic_write_page (uint8_t page_addr); + void nvm_eeprom_split_write_page (uint8_t page_addr); + void nvm_eeprom_fill_buffer_with_value (uint8_t value); + void nvm_eeprom_erase_bytes_in_page (uint8_t page_addr); + void nvm_eeprom_erase_page (uint8_t page_addr); + void nvm_eeprom_erase_bytes_in_all_pages (void); + void nvm_eeprom_erase_all (void); + +//! @} + +/** + * \defgroup nvm_flash_group NVM driver flash handling + * \ingroup nvm_group + * \brief Functions for handling internal flash memory. + * + * The internal flash memory on the XMEGA devices consists of the application + * section, the application table section and the bootloader section. + * All these sections can store program code for the MCU, but if there is + * available space, they can be used for storing other persistent data. + * + * Writing the flash memory can only be done one page at a time. It consists + * of loading the data to the internal page buffer and then running one of + * the write commands. If the page has not been erased before writing, the + * data will not be written correctly. + * + * In order to be able to write to flash memory the programming commands need + * to be run from the boot section. + * - When using IAR this is handled automatically by the linker script. + * - When using GCC this needs to be specified manually in the make files. For + * example the ATxmega128A1 has the boot section at the word address 0x10000 + * the corresponding byte address of 0x20000 needs to be added to the + * config.mk makefile: + * LDFLAGS += -Wl,--section-start=.BOOT=0x20000 + * See the device datasheet for the correct address for other devices. + * + * \note If using GCC and the flash sub-module, remember to configure + * the boot section in the make file. + * + * \note The functions in this module are modifying the NVM.CMD register. + * If the application are using program space access in interrupts + * (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts + * needs to be disabled when running EEPROM access functions. If not + * the program space reads will be corrupted. + * @{ + */ + +/** + * \brief Size of a flash page in bytes + * + * The page size in bytes taken from the toolchain header files. + * + * \note Page size is currently missing from the IAR header files, so it needs + * to be defined in the driver until it is fixed. + */ +#ifdef __DOXYGEN__ +# define FLASH_SIZE +# define FLASH_PAGE_SIZE +#else + +// 8K devices +# if AVR8_PART_IS_DEFINED(ATxmega8E5) +# define FLASH_SIZE (8*1024L) +# define FLASH_PAGE_SIZE (128) + +// 16K devices +# elif AVR8_PART_IS_DEFINED(ATxmega16A4) | \ + AVR8_PART_IS_DEFINED(ATxmega16A4U) | \ + AVR8_PART_IS_DEFINED(ATxmega16D4) | \ + AVR8_PART_IS_DEFINED(ATxmega16C4) +# define FLASH_SIZE (16*1024L) +# define FLASH_PAGE_SIZE (256) + +# elif AVR8_PART_IS_DEFINED(ATxmega16E5) +# define FLASH_SIZE (16*1024L) +# define FLASH_PAGE_SIZE (128) + +// 32K devices +# elif AVR8_PART_IS_DEFINED(ATxmega32A4) | \ + AVR8_PART_IS_DEFINED(ATxmega32A4U) | \ + AVR8_PART_IS_DEFINED(ATxmega32D4) | \ + AVR8_PART_IS_DEFINED(ATxmega32C4) +# define FLASH_SIZE (32*1024L) +# define FLASH_PAGE_SIZE (256) + +# elif AVR8_PART_IS_DEFINED(ATxmega32E5) +# define FLASH_SIZE (32*1024L) +# define FLASH_PAGE_SIZE (128) + +// 64K devices +# elif AVR8_PART_IS_DEFINED(ATxmega64A1) | \ + AVR8_PART_IS_DEFINED(ATxmega64A1U) | \ + AVR8_PART_IS_DEFINED(ATxmega64A3) | \ + AVR8_PART_IS_DEFINED(ATxmega64A3U) | \ + AVR8_PART_IS_DEFINED(ATxmega64A4U) | \ + AVR8_PART_IS_DEFINED(ATxmega64B1) | \ + AVR8_PART_IS_DEFINED(ATxmega64B3) | \ + AVR8_PART_IS_DEFINED(ATxmega64C3) | \ + AVR8_PART_IS_DEFINED(ATxmega64D3) | \ + AVR8_PART_IS_DEFINED(ATxmega64D4) +# define FLASH_SIZE (64*1024L) +# define FLASH_PAGE_SIZE (256) + +// 128K devices +# elif AVR8_PART_IS_DEFINED(ATxmega128A1) | \ + AVR8_PART_IS_DEFINED(ATxmega128A1U) | \ + AVR8_PART_IS_DEFINED(ATxmega128A3) | \ + AVR8_PART_IS_DEFINED(ATxmega128A3U) | \ + AVR8_PART_IS_DEFINED(ATxmega128C3) | \ + AVR8_PART_IS_DEFINED(ATxmega128D3) | \ + AVR8_PART_IS_DEFINED(ATxmega128D4) +# define FLASH_SIZE (128*1024L) +# define FLASH_PAGE_SIZE (512) + +# elif AVR8_PART_IS_DEFINED(ATxmega128A4U) | \ + AVR8_PART_IS_DEFINED(ATxmega128B1) | \ + AVR8_PART_IS_DEFINED(ATxmega128B3) +# define FLASH_SIZE (128*1024L) +# define FLASH_PAGE_SIZE (256) + +// 192K devices +# elif AVR8_PART_IS_DEFINED(ATxmega192A3U) | \ + AVR8_PART_IS_DEFINED(ATxmega192C3) | \ + AVR8_PART_IS_DEFINED(ATxmega192D3) +# define FLASH_SIZE (192*1024L) +# define FLASH_PAGE_SIZE (512) + +// 256K devices +# elif AVR8_PART_IS_DEFINED(ATxmega256A3) | \ + AVR8_PART_IS_DEFINED(ATxmega256A3U) | \ + AVR8_PART_IS_DEFINED(ATxmega256A3B) | \ + AVR8_PART_IS_DEFINED(ATxmega256A3BU) | \ + AVR8_PART_IS_DEFINED(ATxmega256C3) | \ + AVR8_PART_IS_DEFINED(ATxmega256D3) +# define FLASH_SIZE (256*1024L) +# define FLASH_PAGE_SIZE (512) + +// 384K devices +# elif AVR8_PART_IS_DEFINED(ATxmega384C3) +# define FLASH_SIZE (384*1024L) +# define FLASH_PAGE_SIZE (512) +# elif AVR8_PART_IS_DEFINED(ATxmega384D3) +# define FLASH_SIZE (384*1024L) +# define FLASH_PAGE_SIZE (512) +# else +# error Flash page size needs to be defined. +# endif +#endif + +/** + * Data type for holding flash memory addresses. + * + */ +#if (FLASH_SIZE >= 0x10000UL) // Note: Xmega with 64KB of flash have 4KB boot flash + typedef uint32_t flash_addr_t; +#else + typedef uint16_t flash_addr_t; +#endif + +/** + * Flash pointer type to use for accessing flash memory with IAR + */ +#if (FLASH_SIZE >= 0x10000UL) // Note: Xmega with 64KB of flash have 4KB boot flash +# define IAR_FLASH_PTR __farflash +#else +# define IAR_FLASH_PTR __flash +#endif + +/** + * \brief Load byte from flash memory + * + * Load one word of flash using byte addressing. IAR has __flash pointers + * and GCC have pgm_read_byte_xx functions which load data from flash memory. + * This function used for compatibility between the compilers. + * + * \param addr Byte address to load + * \return Byte from program memory + */ + static inline uint8_t nvm_flash_read_byte (flash_addr_t addr) + { +#if defined(__GNUC__) + return pgm_read_byte_far (addr); +#elif defined(__ICCAVR__) + uint8_t IAR_FLASH_PTR *flashptr = (uint8_t IAR_FLASH_PTR *) addr; + return *flashptr; +#else +# error Unknown compiler +#endif + } + +/** + * \brief Load word from flash memory + * + * Load one word of flash using byte addressing. IAR has __flash pointers + * and GCC have pgm_read_byte_xx functions which load data from flash memory. + * This function used for compatibility between the compilers. + * + * \param addr Byte address to load (last bit is ignored) + * \return Word from program memory + */ + static inline uint16_t nvm_flash_read_word (flash_addr_t addr) + { +#if defined(__GNUC__) + return pgm_read_word_far (addr); +#elif defined(__ICCAVR__) + uint16_t IAR_FLASH_PTR *flashptr = (uint16_t IAR_FLASH_PTR *) addr; + return *flashptr; +#endif + } + + +/** + * \brief Flush flash page buffer + * + * Clear the NVM controller page buffer for flash. This needs to be called + * before using \ref nvm_flash_load_word_to_buffer if it has not already been + * cleared. + * + */ + static inline void nvm_flash_flush_buffer (void) + { + nvm_wait_until_ready (); + nvm_common_spm (0, NVM_CMD_ERASE_FLASH_BUFFER_gc); + } + + +/** + * \brief Load word into flash page buffer + * + * Clear the NVM controller page buffer for flash. This needs to be called + * before using \ref nvm_flash_load_word_to_buffer if it has not already been + * cleared. + * + * \param word_addr Address to store data. The upper bits beyond the page size + * is ignored. \ref FLASH_PAGE_SIZE + * \param data Data word to load into the page buffer + */ + void nvm_flash_load_word_to_buffer (uint32_t word_addr, uint16_t data); + + +/** + * \brief Erase entire application section + * + * Erase all of the application section. + */ + static inline void nvm_flash_erase_app (void) + { + nvm_wait_until_ready (); + nvm_common_spm (0, NVM_CMD_ERASE_APP_gc); + } + +/** + * \brief Erase a page within the application section + * + * Erase one page within the application section + * + * \param page_addr Byte address to the page to delete + */ + static inline void nvm_flash_erase_app_page (flash_addr_t page_addr) + { + nvm_wait_until_ready (); + nvm_common_spm (page_addr, NVM_CMD_ERASE_APP_PAGE_gc); + } + +/** + * \brief Write a page within the application section + * + * Write a page within the application section with the data stored in the + * page buffer. The page needs to be erased before the write to avoid + * corruption of the data written. + * + * \param page_addr Byte address to the page to delete + */ + static inline void nvm_flash_split_write_app_page (flash_addr_t page_addr) + { + nvm_wait_until_ready (); + nvm_common_spm (page_addr, NVM_CMD_WRITE_APP_PAGE_gc); + } + +/** + * \brief Erase and write a page within the application section + * + * Erase and the write a page within the application section with the data + * stored in the page buffer. Erase and write is done in an atomic operation. + * + * \param page_addr Byte address to the page to delete + */ + static inline void nvm_flash_atomic_write_app_page (flash_addr_t page_addr) + { + nvm_wait_until_ready (); + nvm_common_spm (page_addr, NVM_CMD_ERASE_WRITE_APP_PAGE_gc); + } + + void nvm_issue_flash_range_crc (flash_addr_t start_addr, + flash_addr_t end_addr); + + void nvm_flash_read_buffer (flash_addr_t address, void *buf, uint16_t len); + + void nvm_flash_erase_and_write_buffer (flash_addr_t address, + const void *buf, uint16_t len, + bool b_blank_check); + +/** + * \brief Erase a page within the boot section + * + * Erase one page within the boot section + * + * \param page_addr Byte address to the page to delete + */ + static inline void nvm_flash_erase_boot_page (flash_addr_t page_addr) + { + nvm_wait_until_ready (); + nvm_common_spm (page_addr, NVM_CMD_ERASE_BOOT_PAGE_gc); + } + +/** + * \brief Write a page within the boot section + * + * Write a page within the boot section with the data stored in the + * page buffer. The page needs to be erased before the write to avoid + * corruption of the data written. + * + * \param page_addr Byte address to the page to delete + */ + static inline void nvm_flash_split_write_boot_page (flash_addr_t page_addr) + { + nvm_wait_until_ready (); + nvm_common_spm (page_addr, NVM_CMD_WRITE_BOOT_PAGE_gc); + } + +/** + * \brief Erase and write a page within the boot section + * + * Erase and the write a page within the boot section with the data + * stored in the page buffer. Erase and write is done in an atomic operation. + * + * \param page_addr Byte address to the page to delete + */ + static inline void nvm_flash_atomic_write_boot_page (flash_addr_t page_addr) + { + nvm_wait_until_ready (); + nvm_common_spm (page_addr, NVM_CMD_ERASE_WRITE_BOOT_PAGE_gc); + } + + void nvm_user_sig_read_buffer (flash_addr_t address, void *buf, + uint16_t len); + void nvm_user_sig_write_buffer (flash_addr_t address, const void *buf, + uint16_t len, bool b_blank_check); + +/** + * \brief Erase the user calibration section page + * + * Erase the user calibration section page. There is only one page, so no + * parameters are needed. + */ + static inline void nvm_flash_erase_user_section (void) + { + nvm_wait_until_ready (); + nvm_common_spm (0, NVM_CMD_ERASE_USER_SIG_ROW_gc); + } + +/** + * \brief Write the user calibration section page + * + * Write a the user calibration section page with the data stored in the + * page buffer. The page needs to be erased before the write to avoid + * corruption of the data written. There is only one page, so no + * parameters are needed. + */ + static inline void nvm_flash_write_user_page (void) + { + nvm_wait_until_ready (); + nvm_common_spm (0, NVM_CMD_WRITE_USER_SIG_ROW_gc); + } + +//! @} + +/** + * \defgroup nvm_fuse_lock_group NVM driver fuse and lock bits handling + * \ingroup nvm_group + * \brief Functions for reading fuses and writing lock bits. + * + * The Fuses are used to set important system functions and can only be written + * from an external programming interface. The application software can read + * the fuses. The fuses are used to configure reset sources such as Brown-out + * Detector and Watchdog, Start-up configuration, JTAG enable and JTAG user ID. + * + * The Lock bits are used to set protection level on the different flash + * sections. They are used to block read and/or write on the different flash + * sections. Lock bits can be written from en external programmer and from the + * application software to set a more strict protection level, but not to set a + * less strict protection level. Chip erase is the only way to erase the lock + * bits. The lock bits are erased after the rest of the flash memory is erased. + * An unprogrammed fuse or lock bit will have the value one, while a programmed + * fuse or lock bit will have the value zero. + * Both fuses and lock bits are reprogrammable like the Flash Program memory. + * + * \note The functions in this module are modifying the NVM.CMD register. + * If the application are using program space access in interrupts + * (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts + * needs to be disabled when running EEPROM access functions. If not + * the program space reads will be corrupted. + * @{ + */ + +// The different fuse bytes + enum fuse_byte_t + { + FUSEBYTE0 = 0, + FUSEBYTE1 = 1, + FUSEBYTE2 = 2, + FUSEBYTE3 = 3, // not used on current devices + FUSEBYTE4 = 4, + FUSEBYTE5 = 5, + }; + + uint8_t nvm_fuses_read (enum fuse_byte_t fuse); + +/** + * \brief Program the lock bits. + * + * Program the lock bits to the given values. Lock bits can only be programmed + * to a more secure setting than previously programmed. To clear lock bits, a + * flash erase has to be issued. + * + * \param blbb_lock Boot loader section lock bits to program + * \param blba_lock Application section lock bits to program + * \param blbat_lock Application table section lock bits to program + * \param lb_lock Flash/eeprom lock bits to program + */ + static inline void nvm_lock_bits_write (enum NVM_BLBB_enum blbb_lock, + enum NVM_BLBA_enum blba_lock, + enum NVM_BLBAT_enum blbat_lock, + enum NVM_LB_enum lb_lock) + { + nvm_wait_until_ready (); + NVM.DATA0 = + (uint8_t) blbb_lock | (uint8_t) blba_lock | (uint8_t) blbat_lock | + (uint8_t) lb_lock; + nvm_issue_command (NVM_CMD_WRITE_LOCK_BITS_gc); + } + +/** + * \brief Program the BLBB lock bits. + * + * Program the lock bits for the boot loader section (BLBB). Other lock bits + * (BLBA, BLBAT and LB) are not altered (ie. programmed to NOLOCK). + * + * \param blbb_lock Boot loader section lock bits to program + */ + static inline void nvm_blbb_lock_bits_write (enum NVM_BLBB_enum blbb_lock) + { + nvm_lock_bits_write (blbb_lock, NVM_BLBA_NOLOCK_gc, NVM_BLBAT_NOLOCK_gc, + NVM_LB_NOLOCK_gc); + } + +/** + * \brief Program the BLBA lock bits. + * + * Program the lock bits for the application section (BLBA). Other lock bits + * (BLBB, BLBAT and LB) are not altered (ie. programmed to NOLOCK). + * + * \param blba_lock Application section lock bits to program + */ + static inline void nvm_blba_lock_bits_write (enum NVM_BLBA_enum blba_lock) + { + nvm_lock_bits_write (NVM_BLBB_NOLOCK_gc, blba_lock, NVM_BLBAT_NOLOCK_gc, + NVM_LB_NOLOCK_gc); + } + +/** + * \brief Program the BLBAT lock bits. + * + * Program the lock bits for the application table section (BLBAT). Other lock + * bits (BLBB, BLBA and LB) are not altered (ie. programmed to NOLOCK). + * + * \param blbat_lock Application table section lock bits to program + */ + static inline void nvm_blbat_lock_bits_write (enum NVM_BLBAT_enum + blbat_lock) + { + nvm_lock_bits_write (NVM_BLBB_NOLOCK_gc, NVM_BLBA_NOLOCK_gc, blbat_lock, + NVM_LB_NOLOCK_gc); + } + +/** + * \brief Program the LB lock bits. + * + * Program the lock bits for the flash and eeprom (LB). Other lock bits + * (BLBB, BLBA and BLBAT) are not altered (ie. programmed to NOLOCK). + * + * \param lb_lock Flash/eeprom lock bits to program + */ + static inline void nvm_lb_lock_bits_write (enum NVM_LB_enum lb_lock) + { + nvm_lock_bits_write (NVM_BLBB_NOLOCK_gc, NVM_BLBA_NOLOCK_gc, + NVM_BLBAT_NOLOCK_gc, lb_lock); + } + +//! @} + +/** + * \page xmega_nvm_quickstart Quick Start Guide for the XMEGA NVM Driver + * + * This is the quick start guide for the \ref nvm_group "NVM Driver", with + * step-by-step instructions on how to configure and use the driver for + * specific use cases. + * + * The section described below can be compiled into e.g. the main application + * loop or any other function that will need to interface non-volatile memory. + * + * \section xmega_nvm_quickstart_basic Basic usage of the NVM driver + * This section will present three use cases of the NVM driver. The first will + * write a page to EEPROM and verify that it has been written, the second will + * access the BOD-level fuse to verify that the level is correctly set, and the + * third will read a chunk from the user signature row. + * + * \section xmega_nvm_quickstart_eeprom_case Use case 1: EEPROM + * + * The NVM driver has functions for interfacing many types of non-volatile + * memory, including flash, EEPROM, fuses and lock bits. The example code + * below will write a page to the internal EEPROM, and read it back to verify, + * using memory mapped I/O. + * + * \section xmega_nvm_quickstart_eeprom_case_setup_steps Setup steps + * There are no setup steps required for this use case. + * + * \subsection nvm_quickstart_eeprom_case_example_code Example code + * + * \code + * #define EXAMPLE_PAGE 2 + * #define EXAMPLE_ADDR EXAMPLE_PAGE * EEPROM_PAGE_SIZE + * + * uint8_t write_page[EEPROM_PAGE_SIZE]; + * uint8_t read_page[EEPROM_PAGE_SIZE]; + * + * fill_page_with_known_data(write_page); + * fill_page_with_zeroes(read_page); + * + * nvm_eeprom_load_page_to_buffer(write_page); + * nvm_eeprom_atomic_write_page(EXAMPLE_PAGE); + * + * nvm_eeprom_read_buffer(EXAMPLE_ADDR, + * read_page, EEPROM_PAGE_SIZE); + * + * check_if_pages_are_equal(write_page, read_page); + * \endcode + * + * \subsection nvm_quickstart_eeprom_case_workflow Workflow + * + * -# We define where we would like to store our data, and we arbitrarily + * choose page 2 of EEPROM: + * - \code + * #define EXAMPLE_PAGE 2 + * #define EXAMPLE_ADDR EXAMPLE_PAGE * EEPROM_PAGE_SIZE + * \endcode + * -# Define two tables, one which contains the data which we will write, + * and one which we will read the data into: + * - \code + * uint8_t write_page[EEPROM_PAGE_SIZE]; + * uint8_t read_page[EEPROM_PAGE_SIZE]; + * \endcode + * -# Fill the tables with our data, and zero out the read table: + * - \code + * fill_page_with_known_data(write_page); + * fill_page_with_zeroes(read_page); + * \endcode + * - \note These functions are undeclared, you should replace them with + * your own appropriate functions. + * -# We load our page into a temporary EEPROM page buffer: + * - \code + * nvm_eeprom_load_page_to_buffer(write_page); + * \endcode + * - \attention The function used above will not work if memory mapping + * is enabled. + * -# Do an atomic write of the page from buffer into the specified page: + * - \code + * nvm_eeprom_atomic_write_page(EXAMPLE_PAGE); + * \endcode + * - \note The function \ref nvm_eeprom_atomic_write_page() erases the + * page before writing the new one. For non-atomic (split) + * writing without deleting, see \ref nvm_eeprom_split_write_page() + * -# Read the page back into our read_page[] table: + * - \code + * nvm_eeprom_read_buffer(EXAMPLE_ADDR, + * read_page, EEPROM_PAGE_SIZE); + * \endcode + * -# Verify that the page is equal to the one that was written earlier: + * - \code + * check_if_pages_are_equal(write_page, read_page); + * \endcode + * - \note This function is not declared, you should replace it with your + * own appropriate function. + * + * \section xmega_nvm_quickstart_fuse_case Use case 2: Fuses + * + * The NVM driver has functions for reading fuses. + * See \ref nvm_fuse_lock_group. + * + * We would like to check whether the Brown-out Detection level is set to + * 2.1V. This is set by programming the fuses when the chip is connected + * to a suitable programmer. The fuse is a part of FUSEBYTE5. If the BODLVL + * is correct, we turn on LED0. + * + * \section xmega_nvm_quickstart_fuse_case_setup_steps Setup steps + * There are no setup steps required for this use case. + * + * \subsection nvm_quickstart_fuse_case_example_code Example code + * \code + * uint8_t fuse_value; + * fuse_value = nvm_fuses_read(FUSEBYTE5); + * + * if ((fuse_value & NVM_FUSES_BODLVL_gm) == BODLVL_2V1_gc) { + * gpio_set_pin_low(LED0_GPIO); + * } + * \endcode + * + * \subsection nvm_quickstart_fuse_case_workflow Workflow + * + * -# Create a variable to store the fuse contents: + * - \code + * uint8_t fuse_value; + * \endcode + * -# The fuse value we are interested in, BODLVL, is stored in FUSEBYTE5. + * We call the function \ref nvm_fuses_read() to read the fuse into our + * variable: + * - \code + * fuse_value = nvm_fuses_read(FUSEBYTE5); + * \endcode + * -# This ends the reading portion, but we would like to see whether the + * BOD-level is correct, and if it is, light up an LED: + * - \code + * if ((fuse_value & NVM_FUSES_BODLVL_gm) == BODLVL_2V1_gc) { + * gpio_set_pin_low(LED0_GPIO); + * } + * \endcode + * + * \section xmega_nvm_quickstart_signature_case Use case 3: Signature row + * + * The NVM driver has functions for reading the signature row of the device. + * Here we will simply read 16 bytes from the user signature row, assuming + * we need what is stored there. + * + * \section xmega_nvm_quickstart_signature_row_setup_steps Setup steps + * There are no setup steps required for this use case. + * + * \subsection xmega_nvm_quickstart_signature_row_example_code Example code + * + * \code + * #define START_ADDR 0x10 + * #define DATA_LENGTH 16 + * + * uint8_t values[LENGTH]; + * uint8_t i; + * + * for (i = 0; i < DATA_LENGTH; i++) { + * values[i] = nvm_read_user_signature_row(START_ADDR + i); + * } + * \endcode + * + * \subsection nvm_quickstart_signature_case_workflow Workflow + * + * -# Define starting address and length of data segment, and create + * variables needed to store and process the data: + * - \code + * #define START_ADDR 0x10 + * #define DATA_LENGTH 16 + * + * uint8_t values[LENGTH]; + * uint8_t i; + * \endcode + * -# Iterate through the user signature row, and store our desired data: + * - \code + * for (i = 0; i < DATA_LENGTH; i++) { + * values[i] = nvm_read_user_signature_row(START_ADDR + i); + * } + * \endcode + * + */ + +#ifdef __cplusplus +} +#endif + +#endif /* NVM_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/nvm/nvm_asm.s b/bacnet-stack/ports/xplained/ASF/xmega/drivers/nvm/nvm_asm.s new file mode 100644 index 00000000..700cd070 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/nvm/nvm_asm.s @@ -0,0 +1,197 @@ +/** + * \file + * + * \brief Non Volatile Memory controller driver + * + * Copyright (c) 2010 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include + +#if defined(__GNUC__) +//! Value to write to CCP for access to protected IO registers. +# define CCP_SPM_gc 0x9D + +//! NVM busy flag +# define NVM_NVMBUSY_bp 7 + +//! NVM command for loading flash buffer +# define NVM_CMD_LOAD_FLASH_BUFFER_gc 0x23 +#elif defined(__IAR_SYSTEMS_ASM__) +// All values are defined for IAR +#else +# error Unknown assembler +#endif + +#ifndef __DOXYGEN__ + PUBLIC_FUNCTION(nvm_read_byte) +#if defined(__GNUC__) + lds r20, NVM_CMD ; Store NVM command register + mov ZL, r22 ; Load byte index into low byte of Z. + mov ZH, r23 ; Load high byte into Z. + sts NVM_CMD, r24 ; Load prepared command into NVM Command register. + lpm r24, Z ; Perform an LPM to read out byte + sts NVM_CMD, r20 ; Restore NVM command register +#elif defined(__IAR_SYSTEMS_ASM__) + lds r20, NVM_CMD ; Store NVM command register + mov ZL, r18 ; Load byte index into low byte of Z. + mov ZH, r19 ; Load high byte into Z. + sts NVM_CMD, r16 ; Load prepared command into NVM Command register. + lpm r16, Z ; Perform an LPM to read out byte + sts NVM_CMD, r20 ; Restore NVM command register +#endif + + ret + + END_FUNC(nvm_read_byte) + +// IAR forgets about include files after each module, so need to include again +#if defined(__IAR_SYSTEMS_ASM__) +# include +#endif + + /** + * \brief Perform SPM command + */ + PUBLIC_FUNCTION_SEGMENT(nvm_common_spm, BOOT) + +#if defined(__GNUC__) + /** + * For GCC: + * \param address uint32_t r22:r25 + * \param nvm_cmd uint8_t r20 + */ + in r25, RAMPZ ; Store RAMPZ. Highest address byte is ignored, so using that + out RAMPZ, r24 ; Load R24 into RAMPZ + movw ZL, r22 ; Load R22:R23 into Z. + lds r24, NVM_CMD ; Store NVM command register (r24 is no longer needed) + sts NVM_CMD, r20 ; Load prepared command into NVM Command register. + ldi r23, CCP_SPM_gc ; Prepare Protect SPM signature (r23 is no longer needed) + sts CCP, r23 ; Enable SPM operation (this disables interrupts for 4 cycles). + spm ; Self-program. + sts NVM_CMD, r24 ; Restore NVM command register + out RAMPZ, r25 ; Restore RAMPZ register. +#elif defined(__IAR_SYSTEMS_ASM__) + /** + * For IAR: + * \param address uint32_t r16:r19 + * \param nvm_cmd uint8_t r20 + */ + in r19, RAMPZ ; Store RAMPZ. Highest address byte is ignored, so using that + out RAMPZ, r18 ; Load R18 into RAMPZ + movw ZL, r16 ; Load R16:R17 into Z. + lds r18, NVM_CMD ; Store NVM command register (r18 is no longer needed) + sts NVM_CMD, r20 ; Load prepared command into NVM Command register. + ldi r19, CCP_SPM_gc ; Prepare Protect SPM signature (r19 is no longer needed) + sts CCP, r19 ; Enable SPM operation (this disables interrupts for 4 cycles). + spm ; Self-program. + sts NVM_CMD, r18 ; Restore NVM command register + out RAMPZ, r19 ; Restore RAMPZ register. +#endif + + ret + + END_FUNC(nvm_common_spm) + +// IAR forgets about include files after each module, so need to include again +#if defined(__IAR_SYSTEMS_ASM__) +# include +#endif + + /** + * \brief Load byte to page buffer + * + */ + PUBLIC_FUNCTION_SEGMENT(nvm_flash_load_word_to_buffer, BOOT) + +#if defined(__GNUC__) + /** + * For GCC: + * \param word_addr uint32_t r22:r25 + * \param data uint16_t r20:r21 + */ +wait_nvm: + lds r18, NVM_STATUS + sbrc r18, NVM_NVMBUSY_bp + rjmp wait_nvm + + in r25, RAMPZ ; Store RAMPZ. Highest address byte is ignored, so using that + out RAMPZ, r24 ; Load R24 into RAMPZ + movw ZL, r22 ; Load R22:R23 into Z. + + lds r24, NVM_CMD ; Store NVM command register (r24 is no longer needed) + ldi r18, NVM_CMD_LOAD_FLASH_BUFFER_gc + sts NVM_CMD, r18 ; Load prepared command into NVM Command register. + + movw r0, r20 ; Load R20:R21 into R0:R1 + spm ; Self-program. + + clr r1 ; Clear R1 for GCC _zero_reg_ to function properly. + sts NVM_CMD, r24 ; Restore NVM command register + out RAMPZ, r25 ; Restore RAMPZ register. +#elif defined(__IAR_SYSTEMS_ASM__) + /** + * For IAR: + * \param word_addr uint32_t r16:r19 + * \param data uint16_t r20:r21 + */ +wait_nvm: + lds r19, NVM_STATUS + sbrc r19, NVM_NVMBUSY_bp + rjmp wait_nvm + + in r19, RAMPZ ; Store RAMPZ. Highest byte is ignored, so using that + out RAMPZ, r18 ; Load R18 into RAMPZ + movw ZL, r16 ; Load R16:R17 into Z. + + lds r18, NVM_CMD ; Store NVM command register (r18 is no longer needed) + ldi r17, NVM_CMD_LOAD_FLASH_BUFFER_gc + sts NVM_CMD, r17 ; Load prepared command into NVM Command register. + + movw r0, r20 ; Load R20:R21 into R0:R1 + spm ; Self-program. + + sts NVM_CMD, r18 ; Restore NVM command register + out RAMPZ, r19 ; Restore RAMPZ register. +#endif + + ret + + END_FUNC(nvm_flash_load_word_to_buffer) + + END_FILE() +#endif // __DOXYGEN__ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/pmic/pmic.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/pmic/pmic.h new file mode 100644 index 00000000..9c5e9884 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/pmic/pmic.h @@ -0,0 +1,361 @@ +/** + * \file + * + * \brief Programmable Multilevel Interrupt Controller driver + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef PMIC_H +#define PMIC_H + +#include +#include + +/** + * \defgroup pmic_group Programmable Multilevel Interrupt Controller + * + * See \ref xmega_pmic_quickstart. + * + * This is a low-level driver implementation for the AVR XMEGA Programmable + * Multilevel Interrupt Controller. + * + * \note If these functions are used in interrupt service routines (ISRs), any + * non-ISR code or ISR code for lower level interrupts must ensure that the + * operations are atomic, i.e., by disabling interrupts during the function + * calls. + * @{ + */ + +/** + * \brief Interrupt level bitmasks + * + * \note These may be OR'ed, e.g., if multiple levels are to be enabled or + * disabled. + */ +enum pmic_level +{ + PMIC_LVL_LOW = PMIC_LOLVLEN_bm, //!< Low-level interrupts + PMIC_LVL_MEDIUM = PMIC_MEDLVLEN_bm, //!< Medium-level interrupts + PMIC_LVL_HIGH = PMIC_HILVLEN_bm, //!< High-level interrupts + /** + * \brief Non-maskable interrupts + * \note These cannot be enabled nor disabled. + */ + PMIC_LVL_NMI = PMIC_NMIEX_bp, +}; + +//! Interrupt vector locations +enum pmic_vector +{ + PMIC_VEC_APPLICATION, //!< Application section + PMIC_VEC_BOOT, //!< Boot section + PMIC_NR_OF_VECTORS, //!< Number of interrupt vector locations +}; + +//! Interrupt scheduling schemes +enum pmic_schedule +{ + PMIC_SCH_FIXED_PRIORITY, //!< Default, fixed priority scheduling + PMIC_SCH_ROUND_ROBIN, //!< Round-robin scheduling + PMIC_NR_OF_SCHEDULES, //!< Number of interrupt scheduling schemes +}; + +/** + * \brief Initialize the PMIC + * + * Enables all interrupt levels, with vectors located in the application section + * and fixed priority scheduling. + */ +static inline void +pmic_init (void) +{ + PMIC.CTRL = PMIC_LVL_LOW | PMIC_LVL_MEDIUM | PMIC_LVL_HIGH; +} + +/** + * \brief Enable interrupts with specified \a level(s). + * + * \param level Interrupt level(s) to enable. + */ +static inline void +pmic_enable_level (enum pmic_level level) +{ + Assert ((level & PMIC_LVL_NMI)); + + PMIC.CTRL |= level; +} + +/** + * \brief Disable interrupts with specified \a level(s). + * + * \param level Interrupt level(s) to disable. + */ +static inline void +pmic_disable_level (enum pmic_level level) +{ + Assert ((level & PMIC_LVL_NMI)); + + PMIC.CTRL &= ~level; +} + +/** + * \brief Check if specified interrupt \a level(s) is enabled. + * + * \param level Interrupt level(s) to check. + * + * \return True if interrupt level(s) is enabled. + */ +static inline bool +pmic_level_is_enabled (enum pmic_level level) +{ + Assert ((level & PMIC_LVL_NMI)); + + return PMIC.CTRL & level; +} + +/** + * \brief Get currently enabled level(s) + * + * \return Bitmask with currently enabled levels. + */ +static inline enum pmic_level +pmic_get_enabled_levels (void) +{ + return (enum pmic_level) (PMIC.CTRL & (PMIC_LVL_LOW | PMIC_LVL_MEDIUM + | PMIC_LVL_HIGH)); +} + +/** + * \brief Check if an interrupt level(s) is currently executing. + * + * \param level Interrupt level(s) to check. + * + * \return True if interrupt level(s) is currently executing. + */ +static inline bool +pmic_level_is_executing (enum pmic_level level) +{ + return PMIC.STATUS & level; +} + +/** + * \brief Set interrupt scheduling for low-level interrupts. + * + * \param schedule Interrupt scheduling method to set. + * + * \note The low-priority vector, INTPRI, must be set to 0 when round-robin + * scheduling is disabled to return to default interrupt priority order. + */ +static inline void +pmic_set_scheduling (enum pmic_schedule schedule) +{ + Assert (schedule < PMIC_NR_OF_SCHEDULES); + + switch (schedule) + { + case PMIC_SCH_FIXED_PRIORITY: + PMIC.CTRL &= ~PMIC_RREN_bm; + PMIC.INTPRI = 0; + break; + + case PMIC_SCH_ROUND_ROBIN: + PMIC.CTRL |= PMIC_RREN_bm; + break; + + default: + break; + }; +} + +/** + * \brief Set location of interrupt vectors. + * + * \param vector Location to use for interrupt vectors. + */ +static inline void +pmic_set_vector_location (enum pmic_vector vector) +{ + uint8_t ctrl = PMIC.CTRL; + + Assert (vector < PMIC_NR_OF_VECTORS); + + switch (vector) + { + case PMIC_VEC_APPLICATION: + ctrl &= ~PMIC_IVSEL_bm; + break; + + case PMIC_VEC_BOOT: + ctrl |= PMIC_IVSEL_bm; + break; + + default: + break; + } + + ccp_write_io ((uint8_t *) & PMIC.CTRL, ctrl); +} + +//! @} + +/** + * \page xmega_pmic_quickstart Quick start guide for PMIC driver + * + * This is the quick start guide for the \ref pmic_group "PMIC driver" and + * the closely related \ref interrupt_group "global interrupt driver", with + * step-by-step instructions on how to configure and use the drivers in a + * selection of use cases. + * + * The use cases contain several code fragments. The code fragments in the + * steps for setup can be copied into a custom initialization function, while + * the steps for usage can be copied into, e.g., the main application function. + * + * \section pmic_basic_use_case Basic use case + * In this basic use case, the PMIC is configured for: + * - all interrupt levels enabled + * - round-robin scheduling + * + * This will allow for interrupts from other modules being used. + * + * \section pmic_basic_use_case_setup Setup steps + * + * \subsection pmic_basic_use_case_setup_prereq Prerequisites + * For the setup code of this use case to work, the following must + * be added to the project: + * -# Interrupts for the module requiring the PMIC module have to be + * enabled. + * -# An Interrupt Service Routine (ISR) for a given interrupt vector has to be + * defined, where the interrupt vectors available are defined by toolchain and + * listed in the subsection 'Interrupt Vector Summary' in the data sheet. + * \code + * ISR(interrupt_vector){ + * //Interrupt Service Routine + * } + * \endcode + * + * \subsection pmic_basic_use_case_setup_code Example code + * Add to the initialization code: + * \code + * pmic_init(); + * pmic_set_scheduling(PMIC_SCH_ROUND_ROBIN); + * cpu_irq_enable(); + * \endcode + * + * \subsection pmic_basic_use_case_setup_flow Workflow + * -# call the PMIC driver's own init function to enable all interrupt levels: + * - \code pmic_init(); \endcode + * -# enable round-robin instead of fixed priority interrupt scheduling: + * - \code pmic_set_scheduling(PMIC_SCH_ROUND_ROBIN); \endcode + * -# enable interrupts globally: + * - \code cpu_irq_enable(); \endcode + * - \attention Interrupts will not trigger without this step. + * + * \section pmic_use_cases Advanced use cases + * For more advanced use of the PMIC driver, see the following use cases: + * - \subpage pmic_use_case_1 : atomic operations + */ + +/** + * \page pmic_use_case_1 Use case #1 + * + * In this use case, the PMIC is configured for: + * - all interrupt levels enabled + * + * This will allow for interrupts from other modules being used. + * + * This use case shows how to make an operation which consists of multiple + * instructions uninterruptible, i.e., into an atomic operation. This is often + * necessary if there is a risk that data can be accessed by interrupt handlers + * while other code is accessing it, and at least one of them modifies it. + * + * \section pmic_use_case_1_setup Setup steps + * + * \subsection pmic_basic_use_case_setup_prereq Prerequisites + * For the setup code of this use case to work, the following must + * be added to the project: + * -# Interrupts for the module requiring the PMIC module have to be + * enabled. + * -# An Interrupt Service Routine (ISR) for a given interrupt vector has to be + * defined, where the interrupt vectors available are defined by toolchain and + * listed in the subsection 'Interrupt Vector Summary' in the data sheet. + * \code + * ISR(interrupt_vector){ + * //Interrupt Service Routine + * } + * \endcode + * + * \subsection pmic_use_case_1_setup_code Example code + * Add to application initialization: + * \code + * pmic_init(); + * cpu_irq_enable(); + * \endcode + * + * \subsection pmic_use_case_1_setup_flow Workflow + * -# call the PMIC driver's own init function to enable all interrupt levels: + * - \code pmic_init(); \endcode + * -# set global interrupt enable flag: + * - \code cpu_irq_enable(); \endcode + * + * \section pmic_use_case_1_usage Usage steps + * + * \subsection pmic_use_case_1_usage_code Example code + * \code + * Add to application: + * void atomic_operation(void) + * { + * irqflags_t flags; + * + * flags = cpu_irq_save(); + * + * // Uninterruptible block of code + * + * cpu_irq_restore(flags); + * } + * \endcode + * + * \subsection pmic_use_case_1_usage_flow Workflow + * -# allocate temporary storage for interrupt enable: + * - \code irqflags_t flags; \endcode + * -# clear global interrupt enable flag while saving its previous state: + * - \code flags = cpu_irq_save(); \endcode + * -# restore the previous state of global interrupt flag after operation: + * - \code cpu_irq_restore(flags); \endcode + */ + +#endif /* PMIC_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/rtc32/rtc32.c b/bacnet-stack/ports/xplained/ASF/xmega/drivers/rtc32/rtc32.c new file mode 100644 index 00000000..0a1a2396 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/rtc32/rtc32.c @@ -0,0 +1,327 @@ +/** + * \file + * + * \brief AVR XMEGA 32-bit Real Time Counter driver + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include +#include +#include +#include +#include "rtc32.h" + +#ifdef __ICCAVR__ +# define _DWORDREGISTER DWORDREGISTER +#endif + +/** + * \internal + * Workaround for missing CNT, PER and COMP in WinAVR header files + * \todo Remove when header files are fixed if WinAVR release + */ +typedef struct RTC32_struct2 { + register8_t CTRL; + register8_t SYNCCTRL; + register8_t INTCTRL; + register8_t INTFLAGS; + _DWORDREGISTER(CNT); + _DWORDREGISTER(PER); + _DWORDREGISTER(COMP); +} RTC32_t2; + +#undef RTC32 + +/** + * \internal + * Workaround for missing CNT, PER and COMP in WinAVR header files + * \todo Remove when header files are fixed if WinAVR release + */ +#define RTC32 (*(RTC32_t2 *)0x0420) + +#ifdef CONFIG_RTC32_COMPARE_INT_LEVEL +# define RTC32_COMPARE_INT_LEVEL CONFIG_RTC32_COMPARE_INT_LEVEL +#else +# define RTC32_COMPARE_INT_LEVEL RTC32_COMPINTLVL_LO_gc +#endif + +#ifdef CONFIG_RTC32_CLOCK_1024HZ +# define RTC32_CLOCK VBAT_XOSCSEL_bm +#else +# define RTC32_CLOCK 0 +#endif + +/** + * \internal + * \brief Driver private struct + */ +struct rtc_data_struct { + //! Callback function to use on alarm + rtc_callback_t callback; +}; + +/** + * \internal + * \brief Driver private data + */ +struct rtc_data_struct rtc_data; + +/** + * \internal + * \brief Check if RTC32 is busy synchronizing + * + * \retval true Is busy + * \retval false Is ready + */ +static __always_inline bool rtc_is_busy(void) +{ + return RTC32.SYNCCTRL & RTC32_SYNCBUSY_bm; +} + +/** + * \internal + * \brief Get counter + * + * \return Counter value + */ +static inline uint32_t rtc_get_counter(void) +{ + RTC32.SYNCCTRL = RTC32_SYNCCNT_bm; + while (RTC32.SYNCCTRL & RTC32_SYNCCNT_bm); + return RTC32.CNT; +} + +/** + * \brief Set current time + * + * \param time Time value to set + */ +void rtc_set_time(uint32_t time) +{ + RTC32.CTRL = 0; + + while (rtc_is_busy()); + + RTC32.CNT = time; + RTC32.CTRL = RTC32_ENABLE_bm; +} + +/** + * \brief Get current time + * + * \return Current time value + */ +uint32_t rtc_get_time(void) +{ + return rtc_get_counter(); +} + +/** + * \brief Set alarm time + * + * Will set absolute time of the alarm that will call the callback function + * specified by \ref rtc_set_callback on expiration. Alternatively, you may + * use \ref rtc_alarm_has_triggered to check if the alarm has expired. + * + * Any pending alarm will be overwritten with this function. + * + * \param time Absolute time value. See also \ref rtc32_min_alarm_time + * \pre Needs interrupts disabled if used from several contexts + */ +void rtc_set_alarm(uint32_t time) +{ + RTC32.INTCTRL = RTC32_COMPARE_INT_LEVEL; + RTC32.COMP = time; + RTC32.INTFLAGS = RTC32_COMPIF_bm; +} + +/** + * \brief Check if pending alarm has triggered + * + * \retval true Alarm has triggered + * \retval false Alarm is pending + */ +bool rtc_alarm_has_triggered(void) +{ + // Interrupt enable is used on pending alarm + return !(RTC32.INTCTRL & RTC32_COMPARE_INT_LEVEL); +} + +/** + * \brief Set callback to call on alarm + * + * \param callback Callback function pointer + */ +void rtc_set_callback(rtc_callback_t callback) +{ + rtc_data.callback = callback; +} + +/** + * \brief Checks battery backup system status. + * + * This function should be called once after each reset of the device in order + * to determine the current battery backup status. + * This function can not be used to continuously poll the status of the backup + * system during normal operation since most status flags are only latched + * during the power up sequence of the device. + * + * \param first_time_startup Indicates whether or not the VBAT system has been + * started previously. This should be set to \c true upon the first call to this + * function, and \c false upon later calls. Typically, the value for this + * parameter should be stored in, e.g., EEPROM, in order to preserve the value + * when main system power is lost. + * + * \returns Battery backup system status. + */ +enum vbat_status_code rtc_vbat_system_check(bool first_time_startup) +{ + enum vbat_status_code vbat_status; + uint8_t flags = VBAT.STATUS; + + /* Ensure the module is clocked to be able to check the registers */ + sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_RTC); + + /* + * Check if a sufficient voltage was detected on the VBAT input. + * The brown-out detector (BBBOD) will be sampled once when the + * device starts up and the result is visible as the BBPWR flag. + */ + if (flags & VBAT_BBPWR_bm) { + vbat_status = VBAT_STATUS_NO_POWER; + } else { + /* + * We have sufficient power, now we check if a power-on-reset + * (BBPOR) was detected on VBAT. This is visible from the BBPORF + * flag which is also only updated once when the device starts. + */ + if (flags & VBAT_BBPORF_bm) { + if (first_time_startup) { + vbat_status = VBAT_STATUS_INIT; + } else { + vbat_status = VBAT_STATUS_BBPOR; + } + } else if (flags & VBAT_BBBORF_bm) { + vbat_status = VBAT_STATUS_BBBOD; + } else { + VBAT.CTRL = VBAT_ACCEN_bm; + if (flags & VBAT_XOSCFAIL_bm) { + vbat_status = VBAT_STATUS_XOSCFAIL; + } else { + vbat_status = VBAT_STATUS_OK; + } + } + } + return vbat_status; +} + +/** + * \internal + * \brief Initialize VBAT and start 32kHz oscillator + * + * Enables access to the VBAT system, performs a reset, enables the failure + * detection, and starts the oscillator. + * + * The default clock rate to the RTC32 is 1Hz but this can be changed to 1024Hz + * by the user in the module configuration by defining + * \ref CONFIG_RTC32_CLOCK_1024HZ. + */ +static void vbat_init(void) +{ + // Enable access to VBAT + VBAT.CTRL |= VBAT_ACCEN_bm; + + ccp_write_io((void *) &VBAT.CTRL, VBAT_RESET_bm); + + VBAT.CTRL |= VBAT_XOSCFDEN_bm; + /* This delay is needed to give the voltage in the backup system some + * time to stabilize before we turn on the oscillator. If we do not + * have this delay we may get a failure detection. + */ + delay_us(200); + VBAT.CTRL |= VBAT_XOSCEN_bm | RTC32_CLOCK; + while (!(VBAT.STATUS & VBAT_XOSCRDY_bm)); +} + +/** + * \brief Initialize the 32kHz oscillator and RTC32 + * + * Starts up the 32kHz oscillator in the backup system and initializes the + * RTC32. + * + * \note When the backup system is used, the function \ref + * rtc_vbat_system_check should be called to determine if a re-initialization + * must be done. + */ +void rtc_init(void) +{ + sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_RTC); + // Set up VBAT system and start oscillator + vbat_init(); + + // Disable the RTC32 module before setting it up + RTC32.CTRL = 0; + + while (rtc_is_busy()); + + // Set up maximum period and start at 0 + RTC32.PER = 0xffffffff; + RTC32.CNT = 0; + + while (rtc_is_busy()); + + RTC32.INTCTRL = 0; + RTC32.CTRL = RTC32_ENABLE_bm; + + // Make sure it's sync'ed before return + while (rtc_is_busy()); +} + +/** + * \internal + * \brief Compare interrupt used for alarm + * + * Disables the RTC32 interrupts, then calls the alarm callback function if one + * has been set. + */ +ISR(RTC32_COMP_vect) +{ + RTC32.INTCTRL = 0; + if (rtc_data.callback) + rtc_data.callback(rtc_get_time()); +} diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/rtc32/rtc32.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/rtc32/rtc32.h new file mode 100644 index 00000000..18b6a71d --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/rtc32/rtc32.h @@ -0,0 +1,324 @@ + /** + * \file + * + * \brief AVR XMEGA 32-bit Real Time Counter driver definitions + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef DRIVERS_RTC32_RTC32_H +#define DRIVERS_RTC32_RTC32_H + +#include +#include + +/** + * \defgroup rtc32_group 32-bit Real Time Counter (RTC32) + * + * See \ref xmega_rtc32_quickstart. + * + * This is a driver implementation for the XMEGA RTC32. + * + * This driver can be used to keep track of time; setting alarms, with or + * without function callbacks; initializing and checking the battery backup + * system. + * + * \section rtc32_min_alarm_time Minimum allowed alarm time + * + * Due to the RTC32 clock synchronization, there is a minimum alarm time that + * will generate a interrupt. This minimum time is 2 RTC32 clock cycles. + * + * Also, if a new RTC32 clock cycle is imminent at the time of setting the + * alarm, there is a risk that it will be missed even with the value 2. If there + * is a risk that this may occur, it is recommended to use a minimum alarm time + * of 3. + * + * @{ + */ + +/** + * \def CONFIG_RTC32_COMPARE_INT_LEVEL + * \brief Configuration symbol for interrupt level to use on alarm + * + * Define this in \ref conf_rtc32.h as the desired interrupt level, or leave it + * undefined to use the default. + */ +#ifdef __DOXYGEN__ +# define CONFIG_RTC32_COMPARE_INT_LEVEL +#endif + +/** + * \def CONFIG_RTC32_CLOCK_1024HZ + * \brief Configuration symbol for selecting 1024Hz clock instead of 1Hz + * + * Define this in \ref conf_rtc32.h if 1024Hz clock is desired. Otherwise, leave + * it undefined. + */ +#ifdef __DOXYGEN__ +# define CONFIG_RTC32_CLOCK_1024HZ +#endif + +//! \brief Battery backup system status codes +enum vbat_status_code +{ +/** + * \brief Backup system is operating and no errors were detected. + * + * The backup system is configured and had no issues while main power was + * lost. Hence, all data stored in the backup domain is valid. + */ + VBAT_STATUS_OK, + +/** + * \brief No power detected on VBAT. + * + * No power was detected on the VBAT pin and therefore all data within the + * backup system is invalid. + * + * The voltage on the VBAT pin is only sampled after a POR of the device, + * therefore it is not possible to detect any voltage loss on the VBAT pin + * during normal operation of the device. + */ + VBAT_STATUS_NO_POWER, + +/** + * \brief The backup system must be initialized. + * + * A POR was detected on VBAT input, indicating that a supply was connected to + * the VBAT pin. Since this is also the first start-up of the device, it is + * necessary to initialize the RTC32. + */ + VBAT_STATUS_INIT, + +/** + * \brief A POR was detected on the VBAT input. + * + * POR detection also works while the VBAT system is powered from main power, + * but the detection flag is only latched after a POR of the main system. + * A POR can happen when the power is lost and restored again on the VBAT pin + * while main power was also not present, or even when main power was present, + * but in this case the flag will only be latched after the next POR of the main + * system. + * If a POR is detected on VBAT, it should always be treated as if the backup + * system is in an unknown state, i.e., that all data is invalid. + */ + VBAT_STATUS_BBPOR, + +/** + * \brief A brown-out was detected on the VBAT input. + * + * The backup system is in an unknown state and therefore the time in the RTC32 + * is invalid. This can happen when the voltage on VBAT drops below the + * brown-out detection level while main power is absent. + */ + VBAT_STATUS_BBBOD, + +/** + * \brief A failure was detected on the oscillator. + * + * The oscillator stopped for at least TBD period of time and because of that + * we can not rely on the RTC time any more. + * + * \todo Determine minimum period for detection of oscillator outage. + */ + VBAT_STATUS_XOSCFAIL, + +}; + +enum vbat_status_code rtc_vbat_system_check (bool first_time_init); + +/** + * \brief Callback definition for alarm callback + * + * \param time The time of the alarm + */ +typedef void (*rtc_callback_t) (uint32_t time); + +void rtc_set_callback (rtc_callback_t callback); +void rtc_set_time (uint32_t time); +uint32_t rtc_get_time (void); +void rtc_set_alarm (uint32_t time); +bool rtc_alarm_has_triggered (void); + +/** + * \brief Set alarm relative to current time + * + * \param offset Offset to current time. This is minimum value, so the alarm + * might happen at up to one time unit later. See also \ref + * rtc32_min_alarm_time + */ +static inline void +rtc_set_alarm_relative (uint32_t offset) +{ + Assert (offset >= 2); + + rtc_set_alarm (rtc_get_time () + offset); +} + +void rtc_init (void); + +//! @} + +/** + * \page xmega_rtc32_quickstart Quick start guide for RTC32 driver + * + * This is the quick start guide for the \ref rtc32_group "RTC32 driver", with + * step-by-step instructions on how to configure and use the drivers in a + * selection of use cases. + * + * The use cases contain several code fragments. The code fragments in the + * steps for setup can be copied into a custom initialization function, while + * the steps for usage can be copied into, e.g., the main application function. + * + * \section rtc32_basic_use_case Basic use case + * + * \section rtc32_basic_use_case_setup Setup steps + * + * \subsection rtc32_basic_use_case_setup_code Example code + * Add to the initialization code: + * \code + * sysclk_init(); + * rtc_init(); + * \endcode + * + * \subsection rtc32_basic_use_case_setup_flow Workflow + * -# Ensure that conf_rtc.h is present for the driver. + * - \note This configuration file is used by the driver and + * should not be included by the user. + * -# Initialize system clock: + * - \code sysclk_init(); \endcode + * -# Call RTC32 driver's own init function to initialize the 32kHz oscillator + * and RTC32: + * - \code rtc_init(); \endcode + * + * \section rtc32_basic_use_case_usage Usage steps + * + * \subsection rtc32_basic_use_case_usage_code Example code + * Add to, e.g., main loop in application C-file: + * \code + * rtc_get_time(); + * \endcode + * + * \subsection rtc32_basic_use_case_usage_flow Workflow + * -# Get current time of the RTC32: + * - \code rtc_get_time(); \endcode + * + * \section rtc32_use_cases Advanced use cases + * For more advanced use of the RTC32 driver, see the following use cases: + * - \subpage rtc32_use_case_1 : + */ + +/** + * \page rtc32_use_case_1 Use case #1 + * + * This use case shows how to set an alarm for the RTC32. + * + * \section rtc32_use_case_1_setup Setup steps + * + * \subsection rtc32_basic_use_case_setup_prereq Prerequisites + * For the setup code of this use case to work, the following must + * be added to the project: + * -# PMIC for interrupt handling. + * -# Sleep Manager. + * -# A \ref rtc_callback_t "callback" function, called alarm, that + * reschedules the alarm must be provided + * by the user. + * \code + * static void alarm(uint32_t time) + * { + * rtc_set_alarm(2); + * } + * \endcode + * \note Since the next alarm will be rounded up to the next second pass, this + * will actually happen in 3 seconds. + * + * \subsection rtc32_use_case_1_setup_code Example code + * Add to application initialization: + * \code + * pmic_init(); + * sysclk_init(); + * sleepmgr_init(); + * rtc_init(); + * rtc_set_callback(alarm); + * cpu_irq_enable(); + * \endcode + * + * \subsection rtc32_use_case_1_setup_flow Workflow + * -# Ensure that conf_rtc32.h is present for the driver. + * - \note This configuration file is used by the driver and + * should not be included by the user. + * -# Call the init function of the PMIC driver to enable all interrupt levels: + * - \code pmic_init(); \endcode + * -# Initialize system clock: + * - \code sysclk_init(); \endcode + * -# Call the init function of the sleep manager driver to be able to sleep + * waiting for alarm: + * - \code sleepmgr_init(); \endcode + * -# Call RTC32 driver's own init function to initialize the 32kHz oscillator + * and RTC32: + * - \code rtc_init(); \endcode + * -# Set callback function to call on alarm: + * - \code rtc_set_callback(alarm); \endcode + * - \note The callback function alarm must be defined by the user. + * -# Enable interrupts globally: + * - \code cpu_irq_enable(); \endcode + * + * \section rtc32_use_case_1_usage Usage steps + * + * \subsection rtc32_use_case_1_usage_code Example code + * \code + * rtc_set_alarm_relative(3); + * while (true) { + * sleepmgr_enter_sleep(); + * } + * \endcode + * + * \subsection rtc32_use_case_1_usage_flow Workflow + * -# Set the alarm to trigger on next time unit roll over: + * - \code rtc_set_alarm_relative(3); \endcode + * \note The lowest value which is safe to use is 3. The use of 2 could + * happen in a second change, and we would not get an interrupt. A + * value of 3 causes the alarm to be set of in 3-4 seconds. + * -# Sleep between each triggered alarm: + * - \code + * while (true) { + * sleepmgr_enter_sleep(); + * } + * \endcode + */ + +#endif /* DRIVERS_RTC32_RTC32_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/sleep/sleep.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/sleep/sleep.h new file mode 100644 index 00000000..dbd6519a --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/sleep/sleep.h @@ -0,0 +1,169 @@ +/** + * \file + * + * \brief Sleep controller driver + * + * Copyright (c) 2010 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef SLEEP_H +#define SLEEP_H + +#include + +/** + * \defgroup sleep_group Sleep controller driver + * + * This is a low-level driver implementation for the AVR XMEGA sleep controller. + * + * \note To minimize the code overhead, these functions do not feature + * interrupt-protected access since they are likely to be called inside + * interrupt handlers or in applications where such protection is not + * necessary. If such protection is needed, it must be ensured by the calling + * code. + * + * \section xmega_sleep_quickstart_section Quick Start Guide + * See \ref xmega_sleep_quickstart + * @{ + */ + +#if defined(__ICCAVR__) || defined(__DOXYGEN__) +# include +//! Macro for issuing the sleep instruction. +# define sleep_enter() __sleep() + +/** + * \brief Enable sleep + */ +static inline void +sleep_enable (void) +{ + SLEEP.CTRL |= SLEEP_SEN_bm; +} + +/** + * \brief Disable sleep + */ +static inline void +sleep_disable (void) +{ + SLEEP.CTRL &= ~SLEEP_SEN_bm; +} + +#elif defined(__GNUC__) +# include +# define sleep_enter() sleep_cpu() + +#else +# error Unsupported compiler. +#endif + +/** + * \brief Set new sleep mode + * + * \param mode Sleep mode, from the device IO header file. + */ +static inline void +sleep_set_mode (enum SLEEP_SMODE_enum mode) +{ + SLEEP.CTRL = mode | (SLEEP.CTRL & ~SLEEP_SMODE_gm); +} + +//! @} + +/** + * \page xmega_sleep_quickstart Quick Start Guide for the XMEGA Sleep Driver + * + * This is the quick start guide for the \ref sleep_group "Sleep Driver", with + * step-by-step instructions on how to configure and use the driver for a + * specific use case. + * + * The section described below can be copied into, e.g. the main application + * loop or any other function that will need to control and execute different + * sleep modes on the device. + * + * \section xmega_sleep_quickstart_basic Basic usage of the sleep driver + * This use case will prepare the device to enter the Power Down sleep mode and + * then enter the sleep mode. After waking up it will disable sleep. + * + * \section xmega_sleep_basic_usage Usage steps + * \subsection xmega_sleep_basic_usage_code Example code + * Add to, e.g., the main loop in the application C-file: + * \code + * sleep_set_mode(SLEEP_SMODE_PDOWN_gc); + * sleep_enable(); + * sleep_enter(); + * sleep_disable(); + * \endcode + * + * \subsection xmega_sleep_basic_usage Workflow + * -# Set what sleep mode to use, the different sleep modes can be found in the + * device header file under the enum definition SLEEP_SMODE_enum: + * - \code sleep_set_mode(SLEEP_SMODE_PDOWN_gc); \endcode + * -# Enable that the device are allowed to go to sleep: + * - \code sleep_enable(); \endcode + * - \note This function has to be called in order for the device to go to + * sleep. This is a safety feature to stop the device to go to sleep + * unintentionally, even though it is possible to have this enabled at all times + * it is recommended to enable sleep mode only when you intend to go to sleep + * within a few clock cycles. + * -# Enter sleep mode: + * - \code sleep_enter(); \endcode + * - \attention Make sure to enable global interrupt and the interrupt you + * plan to use as wake-up source for your device, do also pay special + * attention to what wake-up sources are available for the different sleep + * modes. Failing to enable interrupts may result in indefinite sleep until + * power is cycled! + * -# When the device is woken from sleep it will execute the interrupt handler + * related to the wakeup-source (interrupt source) and continue on the next line + * of code after the \ref sleep_enter() call. Make sure to disable sleep when + * waking up. + * - \code sleep_disable(); \endcode + * + * \subsection xmega_sleep_basic_sleep_modes Sleep Modes + * Possible sleep modes depend on the device that is used. Please refer to the + * device datasheet and header file to find these definitions. + * + * As an example the ATxmega32A4U device has the following sleep modes: + * - Idle sleep: SLEEP_SMODE_IDLE_gc + * - Power Down: SLEEP_SMODE_PDOWN_gc + * - Power Save: SLEEP_SMODE_PSAVE_gc + * - Standby: SLEEP_SMODE_STDBY_gc + * - Extended standby: SLEEP_SMODE_ESTDBY_gc + */ + +#endif /* SLEEP_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/tc/tc.c b/bacnet-stack/ports/xplained/ASF/xmega/drivers/tc/tc.c new file mode 100644 index 00000000..82c16528 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/tc/tc.c @@ -0,0 +1,1089 @@ +/** + * \file + * + * \brief AVR XMEGA TC Driver + * + * Copyright (c) 2010 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include + +#include "interrupt.h" +#include "compiler.h" +#include "parts.h" + +#include "tc.h" +#include "sysclk.h" +#include "sleepmgr.h" +#include "status_codes.h" + +#if defined(TCC0) || defined(__DOXYGEN__) +//! \internal Local storage of Timer Counter TCC0 interrupt callback function +static tc_callback_t tc_tcc0_ovf_callback; +static tc_callback_t tc_tcc0_err_callback; +static tc_callback_t tc_tcc0_cca_callback; +static tc_callback_t tc_tcc0_ccb_callback; +static tc_callback_t tc_tcc0_ccc_callback; +static tc_callback_t tc_tcc0_ccd_callback; + + +/** + * \internal + * \brief Interrupt handler for Timer Counter C0 overflow + * + * This function will handle interrupt on Timer Counter CO overflow and + * call the callback function. + */ +ISR(TCC0_OVF_vect) +{ + if (tc_tcc0_ovf_callback) { + tc_tcc0_ovf_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter C0 error + * + * This function will handle interrupt on Timer Counter CO error and + * call the callback function. + */ +ISR(TCC0_ERR_vect) +{ + if (tc_tcc0_err_callback) { + tc_tcc0_err_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter C0 Compare/CaptureA + * + * This function will handle interrupt on Timer Counter CO Compare/CaptureA and + * call the callback function. + */ +ISR(TCC0_CCA_vect) +{ + if (tc_tcc0_cca_callback) { + tc_tcc0_cca_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter C0 Compare/CaptureB + * + * This function will handle interrupt on Timer Counter CO Compare/CaptureB and + * call the callback function. + */ +ISR(TCC0_CCB_vect) +{ + if (tc_tcc0_ccb_callback) { + tc_tcc0_ccb_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter C0 Compare/CaptureC + * + * This function will handle interrupt on Timer Counter CO Compare/CaptureC and + * call the callback function. + */ +ISR(TCC0_CCC_vect) +{ + if (tc_tcc0_ccc_callback) { + tc_tcc0_ccc_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter C0 Compare/CaptureD + * + * This function will handle interrupt on Timer Counter CO Compare/CaptureD and + * call the callback function. + */ +ISR(TCC0_CCD_vect) +{ + if (tc_tcc0_ccd_callback) { + tc_tcc0_ccd_callback(); + } +} + +#endif + +#if defined(TCC1) || defined(__DOXYGEN__) +//! \internal Local storage of Timer Counter TCC1 interrupt callback function +static tc_callback_t tc_tcc1_ovf_callback; +static tc_callback_t tc_tcc1_err_callback; +static tc_callback_t tc_tcc1_cca_callback; +static tc_callback_t tc_tcc1_ccb_callback; + +/** + * \internal + * \brief Interrupt handler for Timer Counter C1 overflow + * + * This function will handle interrupt on Timer Counter C1 overflow and + * call the callback function. + */ +ISR(TCC1_OVF_vect) +{ + if (tc_tcc1_ovf_callback) { + tc_tcc1_ovf_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter C1 error + * + * This function will handle interrupt on Timer Counter C1 error and + * call the callback function. + */ +ISR(TCC1_ERR_vect) +{ + if (tc_tcc1_err_callback) { + tc_tcc1_err_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter C1 Compare/CaptureA + * + * This function will handle interrupt on Timer Counter C1 Compare/CaptureA and + * call the callback function. + */ +ISR(TCC1_CCA_vect) +{ + if (tc_tcc1_cca_callback) { + tc_tcc1_cca_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter C1 Compare/CaptureB + * + * This function will handle interrupt on Timer Counter C1 Compare/CaptureB and + * call the callback function. + */ +ISR(TCC1_CCB_vect) +{ + if (tc_tcc1_ccb_callback) { + tc_tcc1_ccb_callback(); + } +} + +#endif + +#if defined(TCD0) || defined(__DOXYGEN__) +//! \internal Local storage of Timer Counter TCD0 interrupt callback function +static tc_callback_t tc_tcd0_ovf_callback; +static tc_callback_t tc_tcd0_err_callback; +static tc_callback_t tc_tcd0_cca_callback; +static tc_callback_t tc_tcd0_ccb_callback; +static tc_callback_t tc_tcd0_ccc_callback; +static tc_callback_t tc_tcd0_ccd_callback; + + +/** + * \internal + * \brief Interrupt handler for Timer Counter D0 overflow + * + * This function will handle interrupt on Timer Counter D0 overflow and + * call the callback function. + */ +ISR(TCD0_OVF_vect) +{ + if (tc_tcd0_ovf_callback) { + tc_tcd0_ovf_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter D0 error + * + * This function will handle interrupt on Timer Counter D0 error and + * call the callback function. + */ +ISR(TCD0_ERR_vect) +{ + if (tc_tcd0_err_callback) { + tc_tcd0_err_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter D0 Compare/CaptureA + * + * This function will handle interrupt on Timer Counter D0 Compare/CaptureA and + * call the callback function. + */ +ISR(TCD0_CCA_vect) +{ + if (tc_tcd0_cca_callback) { + tc_tcd0_cca_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter D0 Compare/CaptureB + * + * This function will handle interrupt on Timer Counter D0 Compare/CaptureB and + * call the callback function. + */ +ISR(TCD0_CCB_vect) +{ + if (tc_tcd0_ccb_callback) { + tc_tcd0_ccb_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter D0 Compare/CaptureC + * + * This function will handle interrupt on Timer Counter D0 Compare/CaptureC and + * call the callback function. + */ +ISR(TCD0_CCC_vect) +{ + if (tc_tcd0_ccc_callback) { + tc_tcd0_ccc_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter D0 Compare/CaptureD + * + * This function will handle interrupt on Timer Counter D0 Compare/CaptureD and + * call the callback function. + */ +ISR(TCD0_CCD_vect) +{ + if (tc_tcd0_ccd_callback) { + tc_tcd0_ccd_callback(); + } +} + +#endif + +#if defined(TCD1) || defined(__DOXYGEN__) +//! \internal Local storage of Timer Counter TCD1 interrupt callback function +static tc_callback_t tc_tcd1_ovf_callback; +static tc_callback_t tc_tcd1_err_callback; +static tc_callback_t tc_tcd1_cca_callback; +static tc_callback_t tc_tcd1_ccb_callback; + +/** + * \internal + * \brief Interrupt handler for Timer Counter D1 overflow + * + * This function will handle interrupt on Timer Counter D1 overflow and + * call the callback function. + */ +ISR(TCD1_OVF_vect) +{ + if (tc_tcd1_ovf_callback) { + tc_tcd1_ovf_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter D1 error + * + * This function will handle interrupt on Timer Counter D1 error and + * call the callback function. + */ +ISR(TCD1_ERR_vect) +{ + if (tc_tcd1_err_callback) { + tc_tcd1_err_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter D1 Compare/CaptureA + * + * This function will handle interrupt on Timer Counter D1 Compare/CaptureA and + * call the callback function. + */ +ISR(TCD1_CCA_vect) +{ + if (tc_tcd1_cca_callback) { + tc_tcd1_cca_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter D1 Compare/CaptureB + * + * This function will handle interrupt on Timer Counter D1 Compare/CaptureB and + * call the callback function. + */ +ISR(TCD1_CCB_vect) +{ + if (tc_tcd1_ccb_callback) { + tc_tcd1_ccb_callback(); + } +} + +#endif + + +#if defined(TCE0) || defined(__DOXYGEN__) +//! \internal Local storage of Timer Counter TCE0 interrupt callback function +static tc_callback_t tc_tce0_ovf_callback; +static tc_callback_t tc_tce0_err_callback; +static tc_callback_t tc_tce0_cca_callback; +static tc_callback_t tc_tce0_ccb_callback; +static tc_callback_t tc_tce0_ccc_callback; +static tc_callback_t tc_tce0_ccd_callback; + + +/** + * \internal + * \brief Interrupt handler for Timer Counter E0 overflow + * + * This function will handle interrupt on Timer Counter E0 overflow and + * call the callback function. + */ +ISR(TCE0_OVF_vect) +{ + if (tc_tce0_ovf_callback) { + tc_tce0_ovf_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter E0 error + * + * This function will handle interrupt on Timer Counter E0 error and + * call the callback function. + */ +ISR(TCE0_ERR_vect) +{ + if (tc_tce0_err_callback) { + tc_tce0_err_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter E0 Compare/CaptureA + * + * This function will handle interrupt on Timer Counter E0 Compare/CaptureA and + * call the callback function. + */ +ISR(TCE0_CCA_vect) +{ + if (tc_tce0_cca_callback) { + tc_tce0_cca_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter E0 Compare/CaptureB + * + * This function will handle interrupt on Timer Counter E0 Compare/CaptureB and + * call the callback function. + */ +ISR(TCE0_CCB_vect) +{ + if (tc_tce0_ccb_callback) { + tc_tce0_ccb_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter E0 Compare/CaptureC + * + * This function will handle interrupt on Timer Counter E0 Compare/CaptureC and + * call the callback function. + */ +ISR(TCE0_CCC_vect) +{ + if (tc_tce0_ccc_callback) { + tc_tce0_ccc_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter E0 Compare/CaptureD + * + * This function will handle interrupt on Timer Counter E0 Compare/CaptureD and + * call the callback function. + */ +ISR(TCE0_CCD_vect) +{ + if (tc_tce0_ccd_callback) { + tc_tce0_ccd_callback(); + } +} + +#endif + +#if defined(TCE1) || defined(__DOXYGEN__) +//! \internal Local storage of Timer Counter TCE1 interrupt callback function +static tc_callback_t tc_tce1_ovf_callback; +static tc_callback_t tc_tce1_err_callback; +static tc_callback_t tc_tce1_cca_callback; +static tc_callback_t tc_tce1_ccb_callback; + +/** + * \internal + * \brief Interrupt handler for Timer Counter E1 overflow + * + * This function will handle interrupt on Timer Counter E1 overflow and + * call the callback function. + */ +ISR(TCE1_OVF_vect) +{ + if (tc_tce1_ovf_callback) { + tc_tce1_ovf_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter E1 error + * + * This function will handle interrupt on Timer Counter E1 error and + * call the callback function. + */ +ISR(TCE1_ERR_vect) +{ + if (tc_tce1_err_callback) { + tc_tce1_err_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter E1 Compare/CaptureA + * + * This function will handle interrupt on Timer Counter E1 Compare/CaptureA and + * call the callback function. + */ +ISR(TCE1_CCA_vect) +{ + if (tc_tce1_cca_callback) { + tc_tce1_cca_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter E1 Compare/CaptureB + * + * This function will handle interrupt on Timer Counter E1 Compare/CaptureB and + * call the callback function. + */ +ISR(TCE1_CCB_vect) +{ + if (tc_tce1_ccb_callback) { + tc_tce1_ccb_callback(); + } +} + +#endif + +#if defined(TCF0) || defined(__DOXYGEN__) +//! \internal Local storage of Timer Counter TCF0 interrupt callback function +static tc_callback_t tc_tcf0_ovf_callback; +static tc_callback_t tc_tcf0_err_callback; +static tc_callback_t tc_tcf0_cca_callback; +static tc_callback_t tc_tcf0_ccb_callback; +static tc_callback_t tc_tcf0_ccc_callback; +static tc_callback_t tc_tcf0_ccd_callback; + + +/** + * \internal + * \brief Interrupt handler for Timer Counter E0 overflow + * + * This function will handle interrupt on Timer Counter F0 overflow and + * call the callback function. + */ +ISR(TCF0_OVF_vect) +{ + if (tc_tcf0_ovf_callback) { + tc_tcf0_ovf_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter F0 error + * + * This function will handle interrupt on Timer Counter F0 error and + * call the callback function. + */ +ISR(TCF0_ERR_vect) +{ + if (tc_tcf0_err_callback) { + tc_tcf0_err_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter F0 Compare/CaptureA + * + * This function will handle interrupt on Timer Counter F0 Compare/CaptureA and + * call the callback function. + */ +ISR(TCF0_CCA_vect) +{ + if (tc_tcf0_cca_callback) { + tc_tcf0_cca_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter F0 Compare/CaptureB + * + * This function will handle interrupt on Timer Counter F0 Compare/CaptureB and + * call the callback function. + */ +ISR(TCF0_CCB_vect) +{ + if (tc_tcf0_ccb_callback) { + tc_tcf0_ccb_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter F0 Compare/CaptureC + * + * This function will handle interrupt on Timer Counter F0 Compare/CaptureC and + * call the callback function. + */ +ISR(TCF0_CCC_vect) +{ + if (tc_tcf0_ccc_callback) { + tc_tcf0_ccc_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter F0 Compare/CaptureD + * + * This function will handle interrupt on Timer Counter F0 Compare/CaptureD and + * call the callback function. + */ +ISR(TCF0_CCD_vect) +{ + if (tc_tcf0_ccd_callback) { + tc_tcf0_ccd_callback(); + } +} + +#endif + +#if defined(TCF1) || defined(__DOXYGEN__) +//! \internal Local storage of Timer Counter TCF1 interrupt callback function +static tc_callback_t tc_tcf1_ovf_callback; +static tc_callback_t tc_tcf1_err_callback; +static tc_callback_t tc_tcf1_cca_callback; +static tc_callback_t tc_tcf1_ccb_callback; + +/** + * \internal + * \brief Interrupt handler for Timer Counter F1 overflow + * + * This function will handle interrupt on Timer Counter F1 overflow and + * call the callback function. + */ +ISR(TCF1_OVF_vect) +{ + if (tc_tcf1_ovf_callback) { + tc_tcf1_ovf_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter F1 error + * + * This function will handle interrupt on Timer Counter F1 error and + * call the callback function. + */ +ISR(TCF1_ERR_vect) +{ + if (tc_tcf1_err_callback) { + tc_tcf1_err_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter F1 Compare/CaptureA + * + * This function will handle interrupt on Timer Counter F1 Compare/CaptureA and + * call the callback function. + */ +ISR(TCF1_CCA_vect) +{ + if (tc_tcf1_cca_callback) { + tc_tcf1_cca_callback(); + } +} + +/** + * \internal + * \brief Interrupt handler for Timer Counter F1 Compare/CaptureB + * + * This function will handle interrupt on Timer Counter F1 Compare/CaptureB and + * call the callback function. + */ +ISR(TCF1_CCB_vect) +{ + if (tc_tcf1_ccb_callback) { + tc_tcf1_ccb_callback(); + } +} + +#endif + +/** + * \brief Enable TC + * + * Enables the TC. + * + * \param tc Pointer to TC module + * + * \note + * unmask TC clock (sysclk), but does not configure the TC clock source. + */ +void tc_enable(volatile void *tc) +{ + irqflags_t iflags = cpu_irq_save(); + +#ifdef TCC0 + if ((uintptr_t) tc == (uintptr_t) & TCC0) { + sysclk_enable_module(SYSCLK_PORT_C, SYSCLK_TC0); + sysclk_enable_module(SYSCLK_PORT_C, SYSCLK_HIRES); + } else +#endif +#ifdef TCC1 + if ((uintptr_t) tc == (uintptr_t) & TCC1) { + sysclk_enable_module(SYSCLK_PORT_C, SYSCLK_TC1); + sysclk_enable_module(SYSCLK_PORT_C, SYSCLK_HIRES); + } else +#endif +#ifdef TCD0 + if ((uintptr_t) tc == (uintptr_t) & TCD0) { + sysclk_enable_module(SYSCLK_PORT_D, SYSCLK_TC0); + sysclk_enable_module(SYSCLK_PORT_D, SYSCLK_HIRES); + } else +#endif +#ifdef TCD1 + if ((uintptr_t) tc == (uintptr_t) & TCD1) { + sysclk_enable_module(SYSCLK_PORT_D, SYSCLK_TC1); + sysclk_enable_module(SYSCLK_PORT_D, SYSCLK_HIRES); + } else +#endif +#ifdef TCE0 + if ((uintptr_t) tc == (uintptr_t) & TCE0) { + sysclk_enable_module(SYSCLK_PORT_E, SYSCLK_TC0); + sysclk_enable_module(SYSCLK_PORT_E, SYSCLK_HIRES); + } else +#endif +#ifdef TCE1 + if ((uintptr_t) tc == (uintptr_t) & TCE1) { + sysclk_enable_module(SYSCLK_PORT_E, SYSCLK_TC1); + sysclk_enable_module(SYSCLK_PORT_E, SYSCLK_HIRES); + } else +#endif +#ifdef TCF0 + if ((uintptr_t) tc == (uintptr_t) & TCF0) { + sysclk_enable_module(SYSCLK_PORT_F, SYSCLK_TC0); + sysclk_enable_module(SYSCLK_PORT_F, SYSCLK_HIRES); + } else +#endif +#ifdef TCF1 + if ((uintptr_t) tc == (uintptr_t) & TCF1) { + sysclk_enable_module(SYSCLK_PORT_F, SYSCLK_TC1); + sysclk_enable_module(SYSCLK_PORT_F, SYSCLK_HIRES); + } else +#endif + { + cpu_irq_restore(iflags); + return; + } + sleepmgr_lock_mode(SLEEPMGR_IDLE); + cpu_irq_restore(iflags); +} + + +/** + * \brief Disable TC + * + * Disables the TC. + * + * \param tc Pointer to TC module + * + * \note + * mask TC clock (sysclk). + */ +void tc_disable(volatile void *tc) +{ + irqflags_t iflags = cpu_irq_save(); + + sleepmgr_unlock_mode(SLEEPMGR_IDLE); + +#ifdef TCC0 + if ((uintptr_t) tc == (uintptr_t) & TCC0) { + sysclk_disable_module(SYSCLK_PORT_C, SYSCLK_TC0); + sysclk_disable_module(SYSCLK_PORT_C, SYSCLK_HIRES); + } else +#endif +#ifdef TCC1 + if ((uintptr_t) tc == (uintptr_t) & TCC1) { + sysclk_disable_module(SYSCLK_PORT_C, SYSCLK_TC1); + sysclk_disable_module(SYSCLK_PORT_C, SYSCLK_HIRES); + } else +#endif +#ifdef TCD0 + if ((uintptr_t) tc == (uintptr_t) & TCD0) { + sysclk_disable_module(SYSCLK_PORT_D, SYSCLK_TC0); + sysclk_disable_module(SYSCLK_PORT_D, SYSCLK_HIRES); + } else +#endif +#ifdef TCD1 + if ((uintptr_t) tc == (uintptr_t) & TCD1) { + sysclk_disable_module(SYSCLK_PORT_D, SYSCLK_TC1); + sysclk_disable_module(SYSCLK_PORT_D, SYSCLK_HIRES); + } else +#endif +#ifdef TCE0 + if ((uintptr_t) tc == (uintptr_t) & TCE0) { + sysclk_disable_module(SYSCLK_PORT_E, SYSCLK_TC0); + sysclk_disable_module(SYSCLK_PORT_E, SYSCLK_HIRES); + } else +#endif +#ifdef TCE1 + if ((uintptr_t) tc == (uintptr_t) & TCE1) { + sysclk_disable_module(SYSCLK_PORT_E, SYSCLK_TC1); + sysclk_disable_module(SYSCLK_PORT_E, SYSCLK_HIRES); + } else +#endif +#ifdef TCF0 + if ((uintptr_t) tc == (uintptr_t) & TCF0) { + sysclk_disable_module(SYSCLK_PORT_F, SYSCLK_TC0); + sysclk_disable_module(SYSCLK_PORT_F, SYSCLK_HIRES); + } else +#endif +#ifdef TCF1 + if ((uintptr_t) tc == (uintptr_t) & TCF1) { + sysclk_disable_module(SYSCLK_PORT_F, SYSCLK_TC1); + sysclk_disable_module(SYSCLK_PORT_F, SYSCLK_HIRES); + } else +#endif + { + cpu_irq_restore(iflags); + return; + } + cpu_irq_restore(iflags); +} + +void tc_set_overflow_interrupt_callback(volatile void *tc, + tc_callback_t callback) +{ +#ifdef TCC0 + if ((uintptr_t) tc == (uintptr_t) & TCC0) { + tc_tcc0_ovf_callback = callback; + } else +#endif +#ifdef TCC1 + if ((uintptr_t) tc == (uintptr_t) & TCC1) { + tc_tcc1_ovf_callback = callback; + } else +#endif +#ifdef TCD0 + if ((uintptr_t) tc == (uintptr_t) & TCD0) { + tc_tcd0_ovf_callback = callback; + } else +#endif +#ifdef TCD1 + if ((uintptr_t) tc == (uintptr_t) & TCD1) { + tc_tcd1_ovf_callback = callback; + } else +#endif +#ifdef TCE0 + if ((uintptr_t) tc == (uintptr_t) & TCE0) { + tc_tce0_ovf_callback = callback; + } else +#endif +#ifdef TCE1 + if ((uintptr_t) tc == (uintptr_t) & TCE1) { + tc_tce1_ovf_callback = callback; + } else +#endif +#ifdef TCF0 + if ((uintptr_t) tc == (uintptr_t) & TCF0) { + tc_tcf0_ovf_callback = callback; + } else +#endif +#ifdef TCF1 + if ((uintptr_t) tc == (uintptr_t) & TCF1) { + tc_tcf1_ovf_callback = callback; + } else +#endif + { + } +} + +void tc_set_error_interrupt_callback(volatile void *tc, + tc_callback_t callback) +{ +#ifdef TCC0 + if ((uintptr_t) tc == (uintptr_t) & TCC0) { + tc_tcc0_err_callback = callback; + } else +#endif +#ifdef TCC1 + if ((uintptr_t) tc == (uintptr_t) & TCC1) { + tc_tcc1_err_callback = callback; + } else +#endif +#ifdef TCD0 + if ((uintptr_t) tc == (uintptr_t) & TCD0) { + tc_tcd0_err_callback = callback; + } else +#endif +#ifdef TCD1 + if ((uintptr_t) tc == (uintptr_t) & TCD1) { + tc_tcd1_err_callback = callback; + } else +#endif +#ifdef TCE0 + if ((uintptr_t) tc == (uintptr_t) & TCE0) { + tc_tce0_err_callback = callback; + } else +#endif +#ifdef TCE1 + if ((uintptr_t) tc == (uintptr_t) & TCE1) { + tc_tce1_err_callback = callback; + } else +#endif +#ifdef TCF0 + if ((uintptr_t) tc == (uintptr_t) & TCF0) { + tc_tcf0_err_callback = callback; + } else +#endif +#ifdef TCF1 + if ((uintptr_t) tc == (uintptr_t) & TCF1) { + tc_tcf1_err_callback = callback; + } else +#endif + { + } +} + +void tc_set_cca_interrupt_callback(volatile void *tc, + tc_callback_t callback) +{ +#ifdef TCC0 + if ((uintptr_t) tc == (uintptr_t) & TCC0) { + tc_tcc0_cca_callback = callback; + } else +#endif +#ifdef TCC1 + if ((uintptr_t) tc == (uintptr_t) & TCC1) { + tc_tcc1_cca_callback = callback; + } else +#endif +#ifdef TCD0 + if ((uintptr_t) tc == (uintptr_t) & TCD0) { + tc_tcd0_cca_callback = callback; + } else +#endif +#ifdef TCD1 + if ((uintptr_t) tc == (uintptr_t) & TCD1) { + tc_tcd1_cca_callback = callback; + } else +#endif +#ifdef TCE0 + if ((uintptr_t) tc == (uintptr_t) & TCE0) { + tc_tce0_cca_callback = callback; + } else +#endif +#ifdef TCE1 + if ((uintptr_t) tc == (uintptr_t) & TCE1) { + tc_tce1_cca_callback = callback; + } else +#endif +#ifdef TCF0 + if ((uintptr_t) tc == (uintptr_t) & TCF0) { + tc_tcf0_cca_callback = callback; + } else +#endif +#ifdef TCF1 + if ((uintptr_t) tc == (uintptr_t) & TCF1) { + tc_tcf1_cca_callback = callback; + } else +#endif + { + } +} + +void tc_set_ccb_interrupt_callback(volatile void *tc, + tc_callback_t callback) +{ +#ifdef TCC0 + if ((uintptr_t) tc == (uintptr_t) & TCC0) { + tc_tcc0_ccb_callback = callback; + } else +#endif +#ifdef TCC1 + if ((uintptr_t) tc == (uintptr_t) & TCC1) { + tc_tcc1_ccb_callback = callback; + } else +#endif +#ifdef TCD0 + if ((uintptr_t) tc == (uintptr_t) & TCD0) { + tc_tcd0_ccb_callback = callback; + } else +#endif +#ifdef TCD1 + if ((uintptr_t) tc == (uintptr_t) & TCD1) { + tc_tcd1_ccb_callback = callback; + } else +#endif +#ifdef TCE0 + if ((uintptr_t) tc == (uintptr_t) & TCE0) { + tc_tce0_ccb_callback = callback; + } else +#endif +#ifdef TCE1 + if ((uintptr_t) tc == (uintptr_t) & TCE1) { + tc_tce1_ccb_callback = callback; + } else +#endif +#ifdef TCF0 + if ((uintptr_t) tc == (uintptr_t) & TCF0) { + tc_tcf0_ccb_callback = callback; + } else +#endif +#ifdef TCF1 + if ((uintptr_t) tc == (uintptr_t) & TCF1) { + tc_tcf1_ccb_callback = callback; + } else +#endif + { + } +} + +void tc_set_ccc_interrupt_callback(volatile void *tc, + tc_callback_t callback) +{ +#ifdef TCC0 + if ((uintptr_t) tc == (uintptr_t) & TCC0) { + tc_tcc0_ccc_callback = callback; + } else +#endif + +#ifdef TCD0 + if ((uintptr_t) tc == (uintptr_t) & TCD0) { + tc_tcd0_ccc_callback = callback; + } else +#endif + +#ifdef TCE0 + if ((uintptr_t) tc == (uintptr_t) & TCE0) { + tc_tce0_ccc_callback = callback; + } else +#endif + +#ifdef TCF0 + if ((uintptr_t) tc == (uintptr_t) & TCF0) { + tc_tcf0_ccc_callback = callback; + } else +#endif + { + } + +} + + +void tc_set_ccd_interrupt_callback(volatile void *tc, + tc_callback_t callback) +{ +#ifdef TCC0 + if ((uintptr_t) tc == (uintptr_t) & TCC0) { + tc_tcc0_ccd_callback = callback; + } else +#endif + +#ifdef TCD0 + if ((uintptr_t) tc == (uintptr_t) & TCD0) { + tc_tcd0_ccd_callback = callback; + } else +#endif + +#ifdef TCE0 + if ((uintptr_t) tc == (uintptr_t) & TCE0) { + tc_tce0_ccd_callback = callback; + } else +#endif + +#ifdef TCF0 + if ((uintptr_t) tc == (uintptr_t) & TCF0) { + tc_tcf0_ccd_callback = callback; + } else +#endif + { + } +} diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/tc/tc.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/tc/tc.h new file mode 100644 index 00000000..20c008d5 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/tc/tc.h @@ -0,0 +1,1684 @@ +/** + * \file + * + * \brief AVR XMEGA Timer Counter (TC) driver + * + * Copyright (c) 2010-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _TC_H_ +#define _TC_H_ + +#include +#include +#include "status_codes.h" +#include "pmic.h" +#include +#include + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +/** + * \defgroup tc_group Timer Counter (TC) + * + * See \ref xmega_tc_quickstart + * + * This is a driver for the AVR XMEGA Timer Counter (TC). It provides functions + * for enabling, disabling and configuring the TC modules. + * + * \section dependencies Dependencies + * This driver depends on the following modules: + * - \ref sysclk_group for peripheral clock control. + * - \ref sleepmgr_group for setting allowed sleep mode. + * - \ref interrupt_group for ISR definition and disabling interrupts during + * critical code sections. + * @{ + */ + + + +/** + * \brief Interrupt event callback function type + * + * The interrupt handler can be configured to do a function callback, + * the callback function must match the tc_callback_t type. + * + */ + typedef void (*tc_callback_t) (void); + +//! Timer Counter Capture Compare Channel index + enum tc_cc_channel_t + { + //! Channel A + TC_CCA = 1, + //! Channel B + TC_CCB = 2, + //! Channel C + TC_CCC = 3, + //! Channel D + TC_CCD = 4, + }; + +//! Timer Counter Capture Compare Channel index + enum tc_cc_channel_mask_enable_t + { + //! Channel A Enable mask + TC_CCAEN = TC0_CCAEN_bm, + //! Channel B Enable mask + TC_CCBEN = TC0_CCBEN_bm, + //! Channel C Enable mask + TC_CCCEN = TC0_CCCEN_bm, + //! Channel D Enable mask + TC_CCDEN = TC0_CCDEN_bm, + }; + +//! Timer Counter Direction + enum tc_dir_t + { + //! Counting up + TC_UP = 0, + //! Down Counting B + TC_DOWN = 1 + }; +//! Timer Counter Waveform Generator mode + enum tc_wg_mode_t + { + //! TC in normal Mode + TC_WG_NORMAL = TC_WGMODE_NORMAL_gc, + //! TC in Frequency Generator mode + TC_WG_FRQ = TC_WGMODE_FRQ_gc, + //! TC in single slope PWM mode + TC_WG_SS = TC_WGMODE_SS_gc, + //! TC in dual slope Top PWM mode + TC_WG_DS_T = TC_WGMODE_DS_T_gc, + //! TC in dual slope Top Bottom PWM mode + TC_WG_DS_TB = TC_WGMODE_DS_TB_gc, + //! TC in dual slope Bottom PWM mode + TC_WG_DS_B = TC_WGMODE_DS_B_gc + }; + +//! TC interrupt levels + enum TC_INT_LEVEL_t + { + TC_INT_LVL_OFF = 0x00, + TC_INT_LVL_LO = 0x01, + TC_INT_LVL_MED = 0x02, + TC_INT_LVL_HI = 0x03, + }; + +//! Macro to check if type of passed TC is TC1_t +#define tc_is_tc1(void) ((uint16_t)tc&0x40 ? true : false) +//! Macro to check if type of passed TC is TC0_t +#define tc_is_tc0(void) ((uint16_t)tc&0x40 ? false : true) + +/** + * \brief Enable TC + * + * Enables the TC. + * + * \param tc Pointer to TC module + * + * \note + * unmask TC clock (sysclk), but does not configure the TC clock source. + */ + void tc_enable (volatile void *tc); + +/** + * \brief Disable TC + * + * Disables the TC. + * + * \param tc Pointer to TC module + * + * \note + * mask TC clock (sysclk). + */ + void tc_disable (volatile void *tc); + +/** + * \ingroup tc_group + * \defgroup tc_interrupt_group Timer Counter (TC) interrupt management + * This group provides functions to configure TC module interrupts + * + * + * @{ + */ +/** + * \brief Set TC overflow interrupt callback function + * + * This function allows the caller to set and change the interrupt callback + * function. Without setting a callback function the interrupt handler in the + * driver will only clear the interrupt flags. + * + * \param tc Pointer to the Timer Counter (TC) base address + * \param callback Reference to a callback function + */ + void tc_set_overflow_interrupt_callback (volatile void *tc, + tc_callback_t callback); + +/** + * \brief Set TC error interrupt callback function + * + * This function allows the caller to set and change the interrupt callback + * function. Without setting a callback function the interrupt handler in the + * driver will only clear the interrupt flags. + * + * \param tc Pointer to the Timer Counter (TC) base address + * \param callback Reference to a callback function + */ + void tc_set_error_interrupt_callback (volatile void *tc, + tc_callback_t callback); + +/** + * \brief Set TC Capture Compare Channel A interrupt callback function + * + * This function allows the caller to set and change the interrupt callback + * function. Without setting a callback function the interrupt handler in the + * driver will only clear the interrupt flags. + * + * \param tc Pointer to the Timer Counter (TC) base address + * \param callback Reference to a callback function + */ + void tc_set_cca_interrupt_callback (volatile void *tc, + tc_callback_t callback); + +/** + * \brief Set TC Capture Compare Channel B interrupt callback function + * + * This function allows the caller to set and change the interrupt callback + * function. Without setting a callback function the interrupt handler in the + * driver will only clear the interrupt flags. + * + * \param tc Pointer to the Timer Counter (TC) base address + * \param callback Reference to a callback function + */ + void tc_set_ccb_interrupt_callback (volatile void *tc, + tc_callback_t callback); + +/** + * \brief Set TC Capture Compare Channel C interrupt callback function + * + * This function allows the caller to set and change the interrupt callback + * function. Without setting a callback function the interrupt handler in the + * driver will only clear the interrupt flags. + * + * \param tc Pointer to the Timer Counter (TC) base address + * \param callback Reference to a callback function + */ + void tc_set_ccc_interrupt_callback (volatile void *tc, + tc_callback_t callback); + +/** + * \brief Set TC Capture Compare Channel D interrupt callback function + * + * This function allows the caller to set and change the interrupt callback + * function. Without setting a callback function the interrupt handler in the + * driver will only clear the interrupt flags. + * + * \param tc Pointer to the Timer Counter (TC) base address + * \param callback Reference to a callback function + */ + void tc_set_ccd_interrupt_callback (volatile void *tc, + tc_callback_t callback); +/** + * \brief Configures TC overflow Interrupt level + * + * \param tc Pointer to TC module. + * \param level Overflow interrupt level + * \note Configures OVFINTLVL in INTCTRLA + */ + static inline void tc_set_overflow_interrupt_level (volatile void *tc, + enum TC_INT_LEVEL_t + level) + { + ((TC0_t *) tc)->INTCTRLA = ((TC0_t *) tc)->INTCTRLA & ~TC0_OVFINTLVL_gm; + ((TC0_t *) tc)->INTCTRLA = + ((TC0_t *) tc)->INTCTRLA | (level << TC0_OVFINTLVL_gp); + } + +/** + * \brief Configures TC error Interrupt level + * + * \param tc Pointer to TC module. + * \param level Error interrupt level + * \note Configures ERRINTLVL in INTCTRLA + */ + static inline void tc_set_error_interrupt_level (volatile void *tc, + enum TC_INT_LEVEL_t level) + { + ((TC0_t *) tc)->INTCTRLA = ((TC0_t *) tc)->INTCTRLA & ~TC0_ERRINTLVL_gm; + ((TC0_t *) tc)->INTCTRLA = + ((TC0_t *) tc)->INTCTRLA | (level << TC0_ERRINTLVL_gp); + } + +/** + * \brief Configures TC Capture Compare A Interrupt level + * + * \param tc Pointer to TC module. + * \param level CCA interrupt level + * \note Configures CCAINTLVL in INTCTRLB + */ + static inline void tc_set_cca_interrupt_level (volatile void *tc, + enum TC_INT_LEVEL_t level) + { + ((TC0_t *) tc)->INTCTRLB = ((TC0_t *) tc)->INTCTRLB & ~TC0_CCAINTLVL_gm; + ((TC0_t *) tc)->INTCTRLB = + ((TC0_t *) tc)->INTCTRLB | (level << TC0_CCAINTLVL_gp); + } + +/** + * \brief Configures TC Capture Compare B Interrupt level + * + * \param tc Pointer to TC module. + * \param level CCB interrupt level + * \note Configures CCBINTLVL in INTCTRLB + */ + static inline void tc_set_ccb_interrupt_level (volatile void *tc, + enum TC_INT_LEVEL_t level) + { + ((TC0_t *) tc)->INTCTRLB = ((TC0_t *) tc)->INTCTRLB & ~TC0_CCBINTLVL_gm; + ((TC0_t *) tc)->INTCTRLB = + ((TC0_t *) tc)->INTCTRLB | (level << TC0_CCBINTLVL_gp); + } + +/** + * \brief Configures TC Capture Compare C Interrupt level + * + * \param tc Pointer to TC module. + * \param level CCC interrupt level + * \note Configures CCCINTLVL in INTCTRLB + */ + static inline void tc_set_ccc_interrupt_level (volatile void *tc, + enum TC_INT_LEVEL_t level) + { + ((TC0_t *) tc)->INTCTRLB = ((TC0_t *) tc)->INTCTRLB & ~TC0_CCCINTLVL_gm; + ((TC0_t *) tc)->INTCTRLB = + ((TC0_t *) tc)->INTCTRLB | (level << TC0_CCCINTLVL_gp); + } + + /** + * \brief Configures TC Capture Compare D Interrupt level + * + * \param tc Pointer to TC module. + * \param level CCD interrupt level + * \note Configures CCDINTLVL in INTCTRLB + */ + static inline void tc_set_ccd_interrupt_level (volatile void *tc, + enum TC_INT_LEVEL_t level) + { + ((TC0_t *) tc)->INTCTRLB = ((TC0_t *) tc)->INTCTRLB & ~TC0_CCDINTLVL_gm; + ((TC0_t *) tc)->INTCTRLB = + ((TC0_t *) tc)->INTCTRLB | (level << TC0_CCDINTLVL_gp); + } + +//@} + +/** + * \brief Configure Timer Clock Source + * + * \param tc Pointer to TC module. + * \param TC_CLKSEL_enum Clock source selection + * \note Configuring the clock also starts the timer + */ + static inline void tc_write_clock_source (volatile void *tc, + TC_CLKSEL_t TC_CLKSEL_enum) + { + ((TC0_t *) tc)->CTRLA = + (((TC0_t *) tc)->CTRLA & ~TC0_CLKSEL_gm) | TC_CLKSEL_enum; + } + +/** + * \brief Read Timer Clock Source + * + * \param tc Pointer to TC module. + * \return TC_CLKSEL_enum Clock source selection + */ + static inline TC_CLKSEL_t tc_read_clock_source (volatile void *tc) + { + return (TC_CLKSEL_t) (((TC0_t *) tc)->CTRLA & TC0_CLKSEL_gm); + } + +/** + * \brief Select clock for a specified TC and resolution. + * + * This function configures the clock selection, as prescaled CLKper, for a + * specified TC that gives a resolution at least as high as the one specified. + * The resolution of a TC is synonymous with its clock frequency. + * + * \note It is also possible to clock TCs with event channels. This is not + * handled by this implementation. + * + * \param tc ID of TC to get clock selection for. + * \param resolution Desired resolution for the TC in Hz. + */ + static inline void tc_set_resolution (volatile void *tc, + uint32_t resolution) + { + uint32_t tc_clk_rate = sysclk_get_per_hz (); + + if (resolution <= (tc_clk_rate / 1024)) + { + tc_write_clock_source (tc, TC_CLKSEL_DIV1024_gc); + } + else if (resolution <= (tc_clk_rate / 256)) + { + tc_write_clock_source (tc, TC_CLKSEL_DIV256_gc); + } + else if (resolution <= (tc_clk_rate / 64)) + { + tc_write_clock_source (tc, TC_CLKSEL_DIV64_gc); + } + else if (resolution <= (tc_clk_rate / 8)) + { + tc_write_clock_source (tc, TC_CLKSEL_DIV8_gc); + } + else if (resolution <= (tc_clk_rate / 4)) + { + tc_write_clock_source (tc, TC_CLKSEL_DIV4_gc); + } + else if (resolution <= (tc_clk_rate / 2)) + { + tc_write_clock_source (tc, TC_CLKSEL_DIV2_gc); + } + else + { + tc_write_clock_source (tc, TC_CLKSEL_DIV1_gc); + } + } + +/** + * \brief Get real resolution for a specified TC. + * + * This function returns the resolution which the specified clock selection + * of TC will result in. The resolution of a TC is synonymous with its clock + * frequency. + * + * \note This function does not handle event channel clock selections. + * + * \param tc Pointer of TC module to get resolution for. + * + * \return The resolution of \a tc. + */ + static inline uint32_t tc_get_resolution (volatile void *tc) + { + uint32_t tc_clk_rate = sysclk_get_per_hz (); + switch (tc_read_clock_source (tc)) + { + case TC_CLKSEL_OFF_gc: + tc_clk_rate = 0; + break; + + case TC_CLKSEL_DIV1024_gc: + tc_clk_rate /= 1024; + break; + + case TC_CLKSEL_DIV256_gc: + tc_clk_rate /= 256; + break; + + case TC_CLKSEL_DIV64_gc: + tc_clk_rate /= 64; + break; + + case TC_CLKSEL_DIV8_gc: + tc_clk_rate /= 8; + break; + + case TC_CLKSEL_DIV4_gc: + tc_clk_rate /= 4; + break; + + case TC_CLKSEL_DIV2_gc: + tc_clk_rate /= 2; + break; + + case TC_CLKSEL_DIV1_gc: + break; + + default: + tc_clk_rate = 0; + break; + } + return (tc_clk_rate); + } + +/** + * \brief Configure Timer Direction + * + * \param tc Pointer to TC module. + * \param dir Timer direction : + */ + static inline void tc_set_direction (volatile void *tc, enum tc_dir_t dir) + { + if (dir == TC_UP) + { + ((TC0_t *) tc)->CTRLFCLR |= ~TC0_DIR_bm; + } + else + { + ((TC0_t *) tc)->CTRLFSET |= TC0_DIR_bm; + } + } + +/** + * \brief Write the Counter value of the Timer + * + * \param tc Pointer to TC module. + * \param cnt_value Counter value : + */ + static inline void tc_write_count (volatile void *tc, uint16_t cnt_value) + { + ((TC0_t *) tc)->CNT = cnt_value; + } + +/** + * \brief Reads the Counter value of the Timer + * + * \param tc Pointer to TC module. + * \note Output the Counter value CNT + */ + static inline uint16_t tc_read_count (volatile void *tc) + { + return (((TC0_t *) tc)->CNT); + } + +/** + * \brief Writes the Period value of the Timer + * + * \param tc Pointer to TC module. + * \param per_value Period value : PER + */ + static inline void tc_write_period (volatile void *tc, uint16_t per_value) + { + ((TC0_t *) tc)->PER = per_value; + } + +/** + * \brief Reads the Period value of the Timer + * + * \param tc Pointer to TC module. + * \return Period value : PER + */ + static inline uint16_t tc_read_period (volatile void *tc) + { + return (((TC0_t *) tc)->PER); + } + +/** + * \brief Writes the Period Buffer value of the Timer + * + * \param tc Pointer to TC module. + * \param per_buf Period Buffer value : PERH/PERL + */ + static inline void tc_write_period_buffer (volatile void *tc, + uint16_t per_buf) + { + ((TC0_t *) tc)->PERBUF = per_buf; + } + +/** + * \brief Reads the Period Buffer value of the Timer + * + * \param tc Pointer to TC module. + * \return Period Buffer value : PERH/PERL + */ + static inline uint16_t tc_read_period_buffer (volatile void *tc) + { + return (((TC0_t *) tc)->PERBUF); + } + +/** + * \brief Tests if the Period Buffer is valid + * + * \param tc Pointer to TC module. + * \return period Buffer is valid or not:PERBV + */ + static inline bool tc_period_buffer_is_valid (volatile void *tc) + { + return (((TC0_t *) tc)->CTRLGCLR & TC0_PERBV_bm); + } + +/** + * \brief Enables delay (used for 32bit timer mode) + * + * \param tc Pointer to TC module. + * \note enables Delay mode + */ + static inline void tc_enable_delay (volatile void *tc) + { + ((TC0_t *) tc)->CTRLD = (((TC0_t *) tc)->CTRLD & + ~TC0_EVDLY_bm) | (1 << TC0_EVDLY_bp); + } + +/** + * \brief Disables delay + * + * \param tc Pointer to TC module. + * \note disables Delay mode + */ + static inline void tc_disable_delay (volatile void *tc) + { + ((TC0_t *) tc)->CTRLD = ((TC0_t *) tc)->CTRLD & ~TC0_EVDLY_bm; + } + +/** + * \brief Tests if the Overflow flag is set + * + * \param tc Pointer to TC module. + * \return overflow has occurred or not : OVFIF + */ + static inline bool tc_is_overflow (volatile void *tc) + { + return (((TC0_t *) tc)->INTFLAGS & TC0_OVFIF_bm); + } + +/** + * \brief Clears the Overflow flag + * + * \param tc Pointer to TC module. + * \note OVFIF is cleared + */ + static inline void tc_clear_overflow (volatile void *tc) + { + ((TC0_t *) tc)->INTFLAGS |= TC0_OVFIF_bm; + } + +/** + * \brief Tests if the Error flag is set + * + * \param tc Pointer to TC module. + * \return Error has occurred or not : ERRIF + */ + static inline bool tc_read_error (volatile void *tc) + { + return (((TC0_t *) tc)->INTFLAGS & TC0_ERRIF_bm); + } + +/** + * \brief Clears the Error flag + * + * \param tc Pointer to TC module. + * \note ERRIF is cleared + */ + static inline void tc_clear_error (volatile void *tc) + { + ((TC0_t *) tc)->INTFLAGS |= TC0_ERRIF_bm; + } + +/** + * \brief Restart the Timer + * + * \param tc Pointer to TC module. + * \note CMD[3] in CTRLFSET is set to 1 and CMD[2] in CTRLFCLR is set + */ + static inline void tc_restart (volatile void *tc) + { + ((TC0_t *) tc)->CTRLFSET = TC_CMD_RESTART_gc; + } + +/** + * \brief Reset the Timer + * + * \param tc Pointer to TC module. + * \note CMD[3:2] in CTRLFSET are set to 1 + */ + static inline void tc_reset (volatile void *tc) + { + ((TC0_t *) tc)->CTRLFSET = TC_CMD_RESET_gc; + } + +/** + * \brief Update the Timer + * + * \param tc Pointer to TC module. + * \note CMD[2] in CTRLFSET is set to 1 and CMD[3] in CTRLFCLR is set + */ + static inline void tc_update (volatile void *tc) + { + ((TC0_t *) tc)->CTRLFSET = TC_CMD_UPDATE_gc; + } + +/** + * \brief Configures the Timer in Byte mode + * + * \param tc Pointer to TC module. + * \note Configures BYTEM in CTRLE + */ + static inline void tc_set_8bits_mode (volatile void *tc) + { +#ifdef TC0_BYTEM0_bm + ((TC0_t *) tc)->CTRLE |= TC0_BYTEM0_bm; +#else + ((TC0_t *) tc)->CTRLE |= TC0_BYTEM_bm; +#endif + } + +/** + * \brief Locks the Update of the Buffered registers + * + * \param tc Pointer to TC module. + * + * */ + static inline void tc_lock_update_buffers (volatile void *tc) + { + ((TC0_t *) tc)->CTRLFSET |= TC0_LUPD_bm; + } + +/** + * \brief Unlocks the Update of the Buffered registers + * + * \param tc Pointer to TC module. + * \note Configures LUPD in CTRLFCLR + */ + static inline void tc_unlock_update_buffers (volatile void *tc) + { + ((TC0_t *) tc)->CTRLFCLR |= TC0_LUPD_bm; + } + +/** + * \brief Enables Compare/Capture channel + * + * \param tc Pointer to TC module. + * \param enablemask CC channel + */ + static inline void tc_enable_cc_channels (volatile void *tc, + enum tc_cc_channel_mask_enable_t + enablemask) + { + if (tc_is_tc0 (void *tc)) + { + ((TC0_t *) tc)->CTRLB |= enablemask; + } + else if (tc_is_tc1 (void *tc)) + { + ((TC1_t *) tc)->CTRLB |= enablemask & (TC1_CCAEN_bm | TC1_CCBEN_bm); + } + } + +/** + * \brief Disables Compare/Capture channel + * + * \param tc Pointer to TC module. + * \param disablemask CC channel + */ + static inline void tc_disable_cc_channels (volatile void *tc, + enum tc_cc_channel_mask_enable_t + disablemask) + { + if (tc_is_tc0 (void *tc)) + { + ((TC0_t *) tc)->CTRLB &= ~disablemask; + } + else if (tc_is_tc1 (void *tc)) + { + ((TC1_t *) tc)->CTRLB &= ~(disablemask & TC0_CCAEN_bm & TC0_CCBEN_bm); + } + } + +/** + * \brief Enables Input capture mode + * + * \param tc Pointer to TC module. + * \param eventsource Source for the capture + * \param eventaction Event action capture type + */ + static inline void tc_set_input_capture (volatile void *tc, + TC_EVSEL_t eventsource, + TC_EVACT_t eventaction) + { + ((TC0_t *) tc)->CTRLD &= ~(TC0_EVSEL_gm | TC0_EVACT_gm); + ((TC0_t *) tc)->CTRLD |= ((uint8_t) eventsource | (uint8_t) eventaction); + } + +/** + * \brief Reads the Capture value + * + * \param tc Pointer to TC module. + * \param channel_index Channel x + * \return Read value of CCx + */ + static inline uint16_t tc_read_cc (volatile void *tc, + enum tc_cc_channel_t channel_index) + { + if (tc_is_tc0 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + return (((TC0_t *) tc)->CCA); + case TC_CCB: + return (((TC0_t *) tc)->CCB); + case TC_CCC: + return (((TC0_t *) tc)->CCC); + case TC_CCD: + return (((TC0_t *) tc)->CCD); + } + } + else if (tc_is_tc1 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + return (((TC1_t *) tc)->CCA); + case TC_CCB: + return (((TC1_t *) tc)->CCB); + default: + return (0); + } + } + return (0); + } + +/** + * \brief Writes the CC value + * + * \param tc Pointer to TC module. + * \param channel_index CC Channel + * \param value Counter value + */ + static inline void tc_write_cc (volatile void *tc, + enum tc_cc_channel_t channel_index, + uint16_t value) + { + if (tc_is_tc0 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + ((TC0_t *) tc)->CCA = value; + break; + case TC_CCB: + ((TC0_t *) tc)->CCB = value; + break; + case TC_CCC: + ((TC0_t *) tc)->CCC = value; + break; + case TC_CCD: + ((TC0_t *) tc)->CCD = value; + break; + } + } + else if (tc_is_tc1 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + ((TC1_t *) tc)->CCA = value; + break; + case TC_CCB: + ((TC1_t *) tc)->CCB = value; + break; + default: + return; + } + } + } + +/** + * \brief Writes the Capture/Compare Buffer value + * + * \param tc Pointer to TC module. + * \param channel_index CC Channel + * \param buffer_value Counter Buffer value + */ + static inline void tc_write_cc_buffer (volatile void *tc, + enum tc_cc_channel_t channel_index, + uint16_t buffer_value) + { + if (tc_is_tc0 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + ((TC0_t *) tc)->CCABUF = buffer_value; + break; + case TC_CCB: + ((TC0_t *) tc)->CCBBUF = buffer_value; + break; + case TC_CCC: + ((TC0_t *) tc)->CCCBUF = buffer_value; + break; + case TC_CCD: + ((TC0_t *) tc)->CCDBUF = buffer_value; + break; + } + } + else if (tc_is_tc1 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + ((TC1_t *) tc)->CCABUF = buffer_value; + break; + case TC_CCB: + ((TC1_t *) tc)->CCBBUF = buffer_value; + break; + default: + return; + } + } + } + +/** + * \brief Reads the Capture/Compare Buffer value + * + * \param tc Pointer to TC module. + * \param channel_index CC Channel + * \return CCx Buffer value + */ + static inline uint16_t tc_read_cc_buffer (volatile void *tc, + enum tc_cc_channel_t + channel_index) + { + if (tc_is_tc0 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + return (((TC0_t *) tc)->CCABUF); + case TC_CCB: + return (((TC0_t *) tc)->CCBBUF); + case TC_CCC: + return (((TC0_t *) tc)->CCCBUF); + case TC_CCD: + return (((TC0_t *) tc)->CCDBUF); + } + } + else if (tc_is_tc1 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + return (((TC1_t *) tc)->CCABUF); + case TC_CCB: + return (((TC1_t *) tc)->CCBBUF); + default: + return (0); + } + } + return (0); + } + +/** + * \brief Reports is Capture/Compare Buffer is valid + * + * \param tc Pointer to TC module. + * \param channel_index CC Channel + * \return CCx Buffer is valid or not + */ + static inline bool tc_cc_buffer_is_valid (volatile void *tc, + enum tc_cc_channel_t + channel_index) + { + if (tc_is_tc0 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + return ((TC0_t *) tc)->CTRLGCLR & TC0_CCABV_bm; + case TC_CCB: + return ((TC0_t *) tc)->CTRLGCLR & TC0_CCBBV_bm; + case TC_CCC: + return ((TC0_t *) tc)->CTRLGCLR & TC0_CCCBV_bm; + case TC_CCD: + return ((TC0_t *) tc)->CTRLGCLR & TC0_CCDBV_bm; + } + } + else if (tc_is_tc1 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + return (((TC1_t *) tc)->CTRLGCLR & TC1_CCABV_bm); + case TC_CCB: + return (((TC1_t *) tc)->CTRLGCLR & TC1_CCBBV_bm); + default: + return (0); + } + } + return (0); + } + +/** + * \brief Reports if Capture/Compare interrupt has occurred + * + * \param tc Pointer to TC module. + * \param channel_index CC Channel + * \return CCx Interrupt or not + */ + static inline bool tc_is_cc_interrupt (volatile void *tc, + enum tc_cc_channel_t channel_index) + { + if (tc_is_tc0 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + return (((TC0_t *) tc)->INTFLAGS & TC0_CCAIF_bm); + case TC_CCB: + return (((TC0_t *) tc)->INTFLAGS & TC0_CCBIF_bm); + case TC_CCC: + return (((TC0_t *) tc)->INTFLAGS & TC0_CCCIF_bm); + case TC_CCD: + return (((TC0_t *) tc)->INTFLAGS & TC0_CCDIF_bm); + } + } + else if (tc_is_tc1 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + return (((TC1_t *) tc)->INTFLAGS & TC1_CCAIF_bm); + case TC_CCB: + return (((TC1_t *) tc)->INTFLAGS & TC1_CCBIF_bm); + default: + return (0); + } + } + return (0); + } + +/** + * \brief Clears Capture/Compare interrupt + * + * \param tc Pointer to TC module. + * \param channel_index CC Channel + */ + static inline void tc_clear_cc_interrupt (volatile void *tc, + enum tc_cc_channel_t + channel_index) + { + if (tc_is_tc0 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + ((TC0_t *) tc)->INTFLAGS = TC0_CCAIF_bm; + break; + case TC_CCB: + ((TC0_t *) tc)->INTFLAGS = TC0_CCBIF_bm; + break; + case TC_CCC: + ((TC0_t *) tc)->INTFLAGS = TC0_CCCIF_bm; + break; + case TC_CCD: + ((TC0_t *) tc)->INTFLAGS = TC0_CCDIF_bm; + break; + } + } + else if (tc_is_tc1 (void *tc)) + { + switch (channel_index) + { + case TC_CCA: + ((TC1_t *) tc)->INTFLAGS = TC1_CCAIF_bm; + break; + case TC_CCB: + ((TC1_t *) tc)->INTFLAGS = TC1_CCBIF_bm; + break; + default: + return; + } + } + } + +/** + * \brief Configures TC in the specified Waveform generator mode + * + * \param tc Pointer to TC module. + * \param wgm : waveform generator + */ + static inline void tc_set_wgm (volatile void *tc, enum tc_wg_mode_t wgm) + { + ((TC0_t *) tc)->CTRLB = (((TC0_t *) tc)->CTRLB & ~TC0_WGMODE_gm) | wgm; + } + +/** + * \ingroup tc_group + * \defgroup tc_awex_group AWeX extension driver + * This group provides low level drivers to configure AWeX extension + * @{ + */ + +/** + * \brief AWeX extension enable + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_enable_cwcm (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL |= AWEX_CWCM_bm; + } + +/** + * \brief AWeX extension disable Common waveform mode + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_disable_cwcm (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL &= ~AWEX_CWCM_bm; + } + +/** + * \brief AWeX extension enable pattern generator mode + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_enable_pgm (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL |= AWEX_PGM_bm; + } + +/** + * \brief AWeX extension disable pattern generator mode + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_disable_pgm (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL &= ~AWEX_PGM_bm; + } + +/** + * \brief AWeX extension : enable Deadtime insertion on ccA + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_enable_cca_deadtime (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL |= AWEX_DTICCAEN_bm; + } + +/** + * \brief AWeX extension : disable Deadtime insertion on ccA + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_disable_cca_deadtime (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL &= ~AWEX_DTICCAEN_bm; + } + +/** + * \brief AWeX extension : enable Deadtime insertion on ccB + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_enable_ccb_deadtime (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL |= AWEX_DTICCBEN_bm; + } + +/** + * \brief AWeX extension : disable Deadtime insertion on ccB + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_disable_ccb_deadtime (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL &= ~AWEX_DTICCBEN_bm; + } + +/** + * \brief AWeX extension : enable Deadtime insertion on ccC + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_enable_ccc_deadtime (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL |= AWEX_DTICCCEN_bm; + } + +/** + * \brief AWeX extension : disable Deadtime insertion on ccD + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_disable_ccc_deadtime (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL &= ~AWEX_DTICCCEN_bm; + } + +/** + * \brief AWeX extension : enable Deadtime insertion on ccD + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_enable_ccd_deadtime (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL |= AWEX_DTICCDEN_bm; + } + +/** + * \brief AWeX extension : disable Deadtime insertion on ccD + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_disable_ccd_deadtime (AWEX_t * awex) + { + ((AWEX_t *) awex)->CTRL &= ~AWEX_DTICCDEN_bm; + } +/** + * \brief AWeX extension : configures high side deadtime + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + * \param value : deadtime value + */ + static inline void tc_awex_set_dti_high (AWEX_t * awex, int16_t value) + { + ((AWEX_t *) awex)->DTHS = value; + } +/** + * \brief AWeX extension : configures low side deadtime + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + * \param value : deadtime value + */ + static inline void tc_awex_set_dti_low (AWEX_t * awex, int16_t value) + { + ((AWEX_t *) awex)->DTLS = value; + } +/** + * \brief AWeX extension : configures symmetrical deadtime + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + * \param value : deadtime value + */ + static inline void tc_awex_set_dti_both (AWEX_t * awex, int16_t value) + { + ((AWEX_t *) awex)->DTBOTH = value; + } + +/** + * \brief AWeX extension : configures symmetrical deadtime buffer + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + * \param value : deadtime buffer value + */ + static inline void tc_awex_set_dti_both_buffer (AWEX_t * awex, + int16_t value) + { + ((AWEX_t *) awex)->DTBOTHBUF = value; + } + +/** + * \brief AWeX extension : returns the deadtime buffer high nibble + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + * \return Dead Time High value + */ + static inline int8_t tc_awex_get_dti_high_buffer (AWEX_t * awex) + { + return (((AWEX_t *) awex)->DTHSBUF); + } + +/** + * \brief AWeX extension : returns the deadtime buffer low nibble + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + * \return Dead Time High value + */ + static inline int8_t tc_awex_get_dti_low_buffer (AWEX_t * awex) + { + return (((AWEX_t *) awex)->DTLSBUF); + } + +/** + * \brief AWeX extension : returns if DTI high buffer is valid + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + * \return Dead Time High Buffer valid or not + */ + static inline bool tc_awex_is_dti_high_buffer_valid (AWEX_t * awex) + { + return (((AWEX_t *) awex)->STATUS & AWEX_DTHSBUFV_bm); + } + +/** + * \brief AWeX extension : returns if DTI low buffer is valid + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + * \return Dead Time Low Buffer is valid or not + */ + static inline bool tc_awex_is_dti_low_buffer_valid (AWEX_t * awex) + { + return (((AWEX_t *) awex)->STATUS & AWEX_DTLSBUFV_bm); + } + +/** + * \brief AWeX extension : configures the Fault restart in latched mode + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_fdmode_restart_latched (AWEX_t * awex) + { + ((AWEX_t *) awex)->FDCTRL &= ~AWEX_FDMODE_bm; + } + +/** + * \brief AWeX extension : configures the Fault restart in cycle to cycle mode + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_fdmode_restart_cycle (AWEX_t * awex) + { + ((AWEX_t *) awex)->FDCTRL |= AWEX_FDMODE_bm; + } + +/** + * \brief AWeX extension : returns if fault is detected + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline bool tc_awex_fault_is_detected (AWEX_t * awex) + { + return (((AWEX_t *) awex)->STATUS & AWEX_FDF_bm); + } + +/** + * \brief AWeX extension : clears the Fault detection + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_clear_fault (AWEX_t * awex) + { + ((AWEX_t *) awex)->STATUS = AWEX_FDF_bm; + } + +/** + * \brief AWeX extension : configures fault action + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + * \param fd_act Fault action + */ + static inline void tc_awex_set_fault_detection_action (AWEX_t * + awex, + AWEX_FDACT_t fd_act) + { + ((AWEX_t *) awex)->FDCTRL = (((AWEX_t *) awex)->FDCTRL & ~AWEX_FDACT_gm) | + (fd_act & AWEX_FDACT_gm); + + } + +/** + * \brief AWeX extension : configures fault detection event + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + * \param eventmask Fault detection event + */ + static inline void tc_awex_set_fault_detection_event (AWEX_t * awex, + int8_t eventmask) + { + ((AWEX_t *) awex)->FDEMASK = eventmask; + } + +/** + * \brief AWeX extension : configures the port overdrive + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + * \param value Output override configuration + */ + static inline void tc_awex_set_output_override (AWEX_t * awex, int8_t value) + { + ((AWEX_t *) awex)->OUTOVEN = value; + } + +/** + * \brief AWeX extension : enable fault detection on debug break detection + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_enable_fault_debug_break (AWEX_t * awex) + { + ((AWEX_t *) awex)->FDCTRL &= ~AWEX_FDDBD_bm; + } + +/** + * \brief AWeX extension : disable fault detection on debug break detection + * + * \param awex Pointer to AWeX module (AWEXC or AWEXE) + */ + static inline void tc_awex_disable_fault_debug_break (AWEX_t * awex) + { + ((AWEX_t *) awex)->FDCTRL |= AWEX_FDDBD_bm; + } +//@} +/** + * \ingroup tc_group + * \defgroup tc_hires_group Hi-Res extension driver + * This group provides low level drivers to configure Hi-Res extension + * @{ + */ + +/** + * \brief Hi-Res Extension : configures the Hi-Res + * + * \param hires Pointer to AWeX module (AWEXC or AWEXE) + * \param hi_res_mode HIRES configuration + */ + static inline void tc_hires_set_mode (HIRES_t * hires, + HIRES_HREN_t hi_res_mode) + { + ((HIRES_t *) hires)->CTRLA = hi_res_mode; + } +//@} + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +/** + * \page xmega_tc_quickstart Quick Start Guide for the XMEGA TC Driver + * + * This is the quick start guide for the \ref tc_group , with step-by-step + * instructions on how to configure and use the driver for a specific use case. + * The code examples can be copied into e.g the main application loop or any + * other function that will need to control the timer/counters. + * + * + * \section xmega_tc_qs_use_cases Use cases + * - \ref xmega_tc_qs_ovf + * - \ref xmega_tc_qs_cc + * - \ref xmega_tc_qs_pwm + * + * + * \section xmega_tc_qs_ovf Timer/counter overflow (interrupt based) + * + * This use case will prepare a timer to trigger an interrupt when the timer + * overflows. The interrupt is handled by a cutomisable callback function. + * + * We will setup the timer in this mode: + * - Normal WGM mode (incrementing timer) + * - Use the system clock as clock source + * - No prescaling (clock divider set to 1) + * - Overflow interrupt after 1000 counts. This will be done by setting the top + * value to 1000. + * + * + * \section xmega_tc_qs_ovf_setup Setup steps + * + * \subsection xmega_tc_qs_ovf_usage_prereq Prequisites + * + * For the setup code of this use case to work, the following must + * be added to the project: + * - \ref interrupt_group "Global Interrupt Management" + * - \ref clk_group "Clock Management" + * + * \subsection xmega_tc_qs_ovf_setup_code Example code + * + * Add a callback function that will be executed when the overflow interrupt + * trigger. + * \code + * static void my_callback(void) + * { + * // User code to execute when the overflow occurs here + * } + * \endcode + * Add to, e.g., the main loop in the application C-file: + * \code + * pmic_init(); + * sysclk_init(); + * tc_enable(&TCC0); + * tc_set_overflow_interrupt_callback(&TCC0, my_callback); + * tc_set_wgm(&TCC0, TC_WG_NORMAL); + * tc_write_period(&TCC0, 1000); + * tc_set_overflow_interrupt_level(&TCC0, TC_INT_LVL_LO); + * cpu_irq_enable(); + * tc_write_clock_source(&TCC0, TC_CLKSEL_DIV1_gc); + * \endcode + * + * \subsection xmega_tc_qs_ovf_setup_code_workflow Workflow + * + * -# Enable the interrupt controller: + * - \code pmic_init(); \endcode + * -# Enable the clock system: + * - \code sysclk_init(); \endcode + * -# Enable timer/counter TCC0 + * - \code tc_enable(&TCC0); \endcode + * \note This will enable the clock system for the module + * -# Set the callback function for overflow interrupt + * - \code tc_set_overflow_interrupt_callback(&TCC0, my_callback); \endcode + * \warning This function requires that the my_callback function is defined + * -# Set the desired waveform mode + * - \code tc_set_wgm(&TCC0, TC_WG_NORMAL); \endcode + * \note In this case, we use normal mode where the timer increments it + count value until the TOP value is reached. The timer then reset + its count value to 0. + * -# Set the period + * - \code tc_write_period(&TCC0, 1000); \endcode + * \note This will specify the TOP value of the counter. The timer will + * overflow and reset when this value is reached. + * -# Set the overflow interrupt level + * - \code tc_set_overflow_interrupt_level(&TCC0, TC_INT_LVL_LO); \endcode + * -# Enable interrupts: + * - \code cpu_irq_enable(); \endcode + * -# Set the clock source + * - \code tc_write_clock_source(&TCC0, TC_CLKSEL_DIV1_gc); \endcode + * \warning When the clock source is set, the timer will start counting + * + * \section xmega_tc_qs_ovf_usage Usage steps + * + * - None. The timer will run in the background, and the code written in the + * call back function will execute each time the timer overflows. + * + * + * \section xmega_tc_qs_cc Timer/counter compare match (interrupt based) + * + * This use case will prepare a timer to trigger two independent interrupts + * when it reaches two different compare values. The period of the timer + * is customizable and the two compare matches will be handled by two separate + * interrupts implemented in call back functions. + * + * We will setup the timer in this mode: + * - Normal WGM mode - incrementing timer + * - Use the system clock as clock source + * - No prescaling (divider set to 1) + * - Period of timer 10000 counts + * - Compare match A interrupt trigger after 100 counts + * - Compare match B interrupt trigger after 1000 counts + * - If compare A and compare B match occurs simultaneously, compare B + * should have higher priority + * + * + * \section xmega_tc_qs_cc_setup Setup steps + * + * \subsection xmega_tc_qs_cc_usage_prereq Prequisites + * For the setup code of this use case to work, the following must + * be added to the project: + * - \ref interrupt_group "Global Interrupt Management" + * - \ref clk_group "Clock Management" + * + * \subsection xmega_tc_qs_cc_setup_code Example code + * + * Add two callback functions that will be executed when compare match A and + * compare match B occurs + * \code + * static void my_cca_callback(void) + * { + * // User code here to execute when a channel A compare match occurs + * } + * static void my_ccb_callback(void) + * { + * // User code here to execute when a channel B compare match occurs + * } + * \endcode + * Add to, e.g., the main loop in the application C-file: + * \code + * pmic_init(); + * sysclk_init(); + * cpu_irq_enable(); + * tc_enable(&TCC0); + * tc_set_cca_interrupt_callback(&TCC0, my_cca_callback); + * tc_set_ccb_interrupt_callback(&TCC0, my_ccb_callback); + * tc_set_wgm(&TCC0, TC_WG_NORMAL); + * tc_write_period(&TCC0, 10000); + * tc_write_cc(&TCC0, TC_CCA, 100); + * tc_write_cc(&TCC0, TC_CCB, 1000); + * tc_enable_cc_channels(&TCC0,(TC_CCAEN | TC_CCBEN)); + * tc_set_cca_interrupt_level(&TCC0, TC_INT_LVL_LO); + * tc_set_ccb_interrupt_level(&TCC0, TC_INT_LVL_MED); + * tc_write_clock_source(&TCC0, TC_CLKSEL_DIV1_gc); + * \endcode + * + * \subsection xmega_tc_qs_cc_setup_code_workflow Workflow + * + * -# Enable the interrupt controller: + * - \code pmic_init(); \endcode + * -# Enable the clock system: + * - \code sysclk_init(); \endcode + * -# Enable interrupts: + * - \code cpu_irq_enable(); \endcode + * -# Enable timer/counter TCC0 + * - \code tc_enable(&TCC0); \endcode + * \note This will enable the clock system for the module + * -# Set call back function for CCA interrupt + * - \code tc_set_cca_interrupt_callback(&TCC0, my_cca_callback); \endcode + * \warning This function requires that the call back function is defined + * -# Set call back function for CCB interrupt + * - \code tc_set_ccb_interrupt_callback(&TCC0, my_ccb_callback); \endcode + * \warning This function requires that the call back function is defined + * -# Set the desired waveform mode + * - \code tc_set_wgm(&TCC0, TC_WG_NORMAL); \endcode + * \note In this case, we use normal mode where the timer increments it + count value until the TOP value is reached. The timer then reset + its count value to 0. + * -# Set the period + * - \code tc_write_period(&TCC0, 10000); \endcode + * \note This will specify the TOP value of the counter. The timer will + * overflow and reset when this value is reached. + * -# Set compare match value on CCA + * - \code tc_write_cc(&TCC0, TC_CCA, 100); \endcode + * -# Set compare match value on CCB + * - \code tc_write_cc(&TCC0, TC_CCB, 1000); \endcode + * -# Enable compare channel A and compare channel B + * -\code tc_enable_cc_channels(&TCC0, (TC_CCAEN | TC_CCBEN)); \endcode + * -# Set interrupt level on channel A (low priority, see \ref TC_INT_LEVEL_t) + * - \code tc_set_cca_interrupt_level(&TCC0, TC_INT_LVL_LO); \endcode + * -# Set interrupt level on channel B (medium priority \ref TC_INT_LEVEL_t) + * - \code tc_set_ccb_interrupt_level(&TCC0, TC_INT_LVL_MED); \endcode + * -# Set the clock source + * - \code tc_write_clock_source(&TCC0, TC_CLKSEL_DIV1_gc); \endcode + * \warning When the clock source is set, the timer will start counting + * + * \section xmega_tc_qs_cc_usage Usage steps + * + * - None. The timer will run in the background, and the code written in the + * call back functions will execute each time a compare match occur. + * + * + * \section xmega_tc_qs_pwm Timer/counter PWM + * + * This use case will setup a timer in PWM mode. For more details you can + * also look at the XMEGA PWM service. + * + * We will setup the timer in this mode: + * - Normal WGM mode - incrementing timer + * - Use the 2MHz oscillator as clock source (default) + * - 1Hz PWM frequency (2MHz clock, 1024x prescale, TOP value 1950) + * - 10% duty cycle (1:10 ratio between PER and CC register) + * - Output the PWM signal to a I/O port + * + * \section xmega_tc_qs_pwm_setup Setup steps + * + * \subsection xmega_tc_qs_pwm_usage_prereq Prequisites + * For the setup code of this use case to work, the following must + * be added to the project: + * - \ref clk_group "Clock Management" + * + * \subsection xmega_tc_qs_pwm_setup_code Example code + * + * Add to, e.g., the main loop in the application C-file: + * \code + * board_init(); + * sysclk_init(); + * tc_enable(&TCE0); + * tc_set_wgm(&TCE0, TC_WG_SS); + * tc_write_period(&TCE0, 1950); + * tc_write_cc(&TCE0, TC_CCA, 195); + * tc_enable_cc_channels(&TCE0,TC_CCAEN); + * tc_write_clock_source(&TCE0, TC_CLKSEL_DIV1024_gc); + * \endcode + * + * \subsection xmega_tc_qs_pwm_setup_code_workflow Workflow + * + * -# Ensure that PWM I/O pin is configured as output + * - \code board_init(); \endcode + * \note The board_init(); function configures the I/O pins. If this function + * is not executed, the I/O pin must be configured as output manually + * -# Enable the clock system: + * - \code sysclk_init(); \endcode + * -# Enable timer/counter TCE0 + * - \code tc_enable(&TCE0); \endcode + * \note This will enable the clock system for the module + * -# Set the desired waveform mode + * - \code tc_set_wgm(&TCE0, TC_WG_NORMAL); \endcode + * \note In this case, we use normal mode where the timer increments it + * count value until the TOP value is reached. The timer then reset + * its count value to 0. + * -# Set the period + * - \code tc_write_period(&TCE0, 1950); \endcode + * \note This will specify the TOP value of the counter. The timer will + * overflow and reset when this value is reached. + * -# Set compare match value on CCA + * - \code tc_write_cc(&TCC0, TC_CCA, 195); \endcode + * \note The PWM duty cycle will be the ratio between PER and CCA, which + * is set by the tc_write_period() and tc_write_cc() functions. Use + * tc_write_cc() to change duty cycle run time (e.g to dim a LED). + * When CCA = 0, the duty cycle will be 0%. When CCA = PER (top value) + * the duty cycle will be 100%. + * -# Enable compare channel A + * -\code tc_enable_cc_channels(&TCE0,TC_CCAEN); \endcode + * -# Set the clock source + * - \code tc_write_clock_source(&TCE0, TC_CLKSEL_DIV1024_gc); \endcode + * \warning When the clock source is set, the timer will start counting + * + * \section xmega_tc_qs_pwm_usage Usage steps + * - Use tc_write_cc() to change the duty cycle of the PWM signal + * - Use tc_write_period() to change the PWM frequency + */ + +#endif /* _TC_H_ */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twi_common.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twi_common.h new file mode 100644 index 00000000..ea0961c3 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twi_common.h @@ -0,0 +1,672 @@ +/** + * \file + * + * \brief AVR XMEGA TWI driver common definitions + * + * Copyright (c) 2011-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#ifndef TWI_COMMON_H +#define TWI_COMMON_H + +/* Fix header error in iox32e5.h */ +#ifndef TWI_BRIDGEEN_bm +#define TWI_BRIDGEEN_bm 0x80 /* Bridge Enable bit mask. */ +#endif + +#ifndef TWI_BRIDGEEN_bp +#define TWI_BRIDGEEN_bp 7 /* Bridge Enable bit position. */ +#endif + +#ifndef TWI_SFMPEN_bm +#define TWI_SFMPEN_bm 0x40 /* Slave Fast Mode Plus Enable bit mask. */ +#endif + +#ifndef TWI_SFMPEN_bp +#define TWI_SFMPEN_bp 6 /* Slave Fast Mode Plus Enable bit position. */ +#endif +/* End of: Fix header error in iox32e5.h */ + +/** + * \defgroup group_xmega_drivers_twi TWI - Two-Wire Interface + * + * See \ref xmega_twi_quickstart + * + * Driver for the Two-Wire Interface (TWI). + * Provides functions for configuring and using the TWI in both master and + * slave mode. + * + * \section xmega_twi_quickstart_guide Quick start guide + * See \ref xmega_twi_quickstart + * + * \{ + */ + +/*! + * \brief Input parameters when initializing the twi module mode + */ +typedef struct +{ + //! The baudrate of the TWI bus. + unsigned long speed; + //! The baudrate register value of the TWI bus. + unsigned long speed_reg; + //! The desired address. + char chip; +} +twi_options_t; + +/*! + * \brief Information concerning the data transmission + */ +typedef struct +{ + //! TWI chip address to communicate with. + char chip; + //! TWI address/commands to issue to the other chip (node). + uint8_t addr[3]; + //! Length of the TWI data address segment (1-3 bytes). + int addr_length; + //! Where to find the data to be written. + void *buffer; + //! How many bytes do we want to write. + unsigned int length; + //! Whether to wait if bus is busy (false) or return immediately (true) + bool no_wait; +} +twi_package_t; + +/** + * \} + */ + +/** + * \page xmega_twi_quickstart Quick start guide for XMEGA TWI driver + * + * This is the quick start guide for the + *\ref group_xmega_drivers_twi "TWI Driver", with step-by-step instructions on + * how to configure and use the driver for specific use cases. + * + * The section described below can be compiled into e.g. the main application + * loop or any other function that might use the TWI functionality. + * + * \section xmega_twi_quickstart_basic Basic use case of the TWI driver + * In our basic use case, the TWI driver is used to set up internal + * communication between two TWI modules on the XMEGA A1 Xplained board, since + * this is the most simple way to show functionality without external + * dependencies. TWIC is set up in master mode, and TWIF is set up in slave + * mode, and these are connected together on the board by placing a connection + * between SDA/SCL on J1 to SDA/SCL on J4. + * + * \section xmega_twi_qs_use_cases Specific use case for XMEGA E devices + * - \subpage xmega_twi_xmegae + * + * \section xmega_twi_quickstart_prereq Prerequisites + * The \ref sysclk_group module is required to enable the clock to the TWI + * modules. The \ref group_xmega_drivers_twi_twim "TWI Master" driver and + * \ref group_xmega_drivers_twi_twis "TWI Slave" driver must also be included. + * + * \section xmega_twi_quickstart_setup Setup + * When the \ref sysclk_group module has been included, it must be initialized: + * \code + * sysclk_init(); + * \endcode + * + * \section xmega_twi_quickstart_use_case Use case + * + * \subsection xmega_twi_quickstart_use_case_example_code Example code + * + * \code + * #define TWI_MASTER TWIC + * #define TWI_MASTER_PORT PORTC + * #define TWI_SLAVE TWIF + * #define TWI_SPEED 50000 + * #define TWI_MASTER_ADDR 0x50 + * #define TWI_SLAVE_ADDR 0x60 + * + * #define DATA_LENGTH 8 + * + * TWI_Slave_t slave; + * + * uint8_t data[DATA_LENGTH] = { + * 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f + * }; + * + * uint8_t recv_data[DATA_LENGTH] = { + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + * }; + * + * twi_options_t m_options = { + * .speed = TWI_SPEED, + * .chip = TWI_MASTER_ADDR, + * .speed_reg = TWI_BAUD(sysclk_get_cpu_hz(), TWI_SPEED) + * }; + * + * static void slave_process(void) { + * int i; + * + * for(i = 0; i < DATA_LENGTH; i++) { + * recv_data[i] = slave.receivedData[i]; + * } + * } + * + * ISR(TWIF_TWIS_vect) { + * TWI_SlaveInterruptHandler(&slave); + * } + * + * void send_and_recv_twi() + * { + * twi_package_t packet = { + * .addr_length = 0, + * .chip = TWI_SLAVE_ADDR, + * .buffer = (void *)data, + * .length = DATA_LENGTH, + * .no_wait = false + * }; + * + * uint8_t i; + * + * TWI_MASTER_PORT.PIN0CTRL = PORT_OPC_WIREDANDPULL_gc; + * TWI_MASTER_PORT.PIN1CTRL = PORT_OPC_WIREDANDPULL_gc; + * + * irq_initialize_vectors(); + * + * sysclk_enable_peripheral_clock(&TWI_MASTER); + * twi_master_init(&TWI_MASTER, &m_options); + * twi_master_enable(&TWI_MASTER); + * + * sysclk_enable_peripheral_clock(&TWI_SLAVE); + * TWI_SlaveInitializeDriver(&slave, &TWI_SLAVE, *slave_process); + * TWI_SlaveInitializeModule(&slave, TWI_SLAVE_ADDR, + * TWI_SLAVE_INTLVL_MED_gc); + * + * for (i = 0; i < TWIS_SEND_BUFFER_SIZE; i++) { + * slave.receivedData[i] = 0; + * } + * + * cpu_irq_enable(); + * + * twi_master_write(&TWI_MASTER, &packet); + * + * do { + * // Nothing + * } while(slave.result != TWIS_RESULT_OK); + * } + * \endcode + * + * \subsection xmega_twi_quickstart_use_case_workflow Workflow + * We first create some definitions. TWI master and slave, speed, and + * addresses: + * \code + * #define TWI_MASTER TWIC + * #define TWI_MASTER_PORT PORTC + * #define TWI_SLAVE TWIF + * #define TWI_SPEED 50000 + * #define TWI_MASTER_ADDR 0x50 + * #define TWI_SLAVE_ADDR 0x60 + * + * #define DATA_LENGTH 8 + * \endcode + * + * We create a handle to contain information about the slave module: + * \code + * TWI_Slave_t slave; + * \endcode + * + * We create two variables, one which contains data that will be transmitted, + * and one which will contain the received data: + * \code + * uint8_t data[DATA_LENGTH] = { + * 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f + * }; + * + * uint8_t recv_data[DATA_LENGTH] = { + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + * }; + * \endcode + * + * Options for the TWI module initialization procedure are given below: + * \code + * twi_options_t m_options = { + * .speed = TWI_SPEED, + * .chip = TWI_MASTER_ADDR, + * .speed_reg = TWI_BAUD(sysclk_get_cpu_hz(), TWI_SPEED) + * }; + * \endcode + * + * The TWI slave will fire an interrupt when it has received data, and the + * function below will be called, which will copy the data from the driver + * to our recv_data buffer: + * \code + * static void slave_process(void) { + * int i; + * + * for(i = 0; i < DATA_LENGTH; i++) { + * recv_data[i] = slave.receivedData[i]; + * } + * } + * \endcode + * + * Set up the interrupt handler: + * \code + * ISR(TWIF_TWIS_vect) { + * TWI_SlaveInterruptHandler(&slave); + * } + * \endcode + * + * We create a packet for the data that we will send to the slave TWI: + * \code + * twi_package_t packet = { + * .addr_length = 0, + * .chip = TWI_SLAVE_ADDR, + * .buffer = (void *)data, + * .length = DATA_LENGTH, + * .no_wait = false + * }; + * \endcode + * + * We need to set SDA/SCL pins for the master TWI to be wired and + * enable pull-up: + * \code + * TWI_MASTER_PORT.PIN0CTRL = PORT_OPC_WIREDANDPULL_gc; + * TWI_MASTER_PORT.PIN1CTRL = PORT_OPC_WIREDANDPULL_gc; + * \endcode + * + * We enable all interrupt levels: + * \code + * irq_initialize_vectors(); + * \endcode + * + * We enable the clock to the master module, and initialize it with the + * options we described before: + * \code + * sysclk_enable_peripheral_clock(&TWI_MASTER); + * twi_master_init(&TWI_MASTER, &m_options); + * twi_master_enable(&TWI_MASTER); + * \endcode + * + * We do the same for the slave, using the slave portion of the driver, + * passing through the slave_process function, its address, and set medium + * interrupt level: + * \code + * sysclk_enable_peripheral_clock(&TWI_SLAVE); + * TWI_SlaveInitializeDriver(&slave, &TWI_SLAVE, *slave_process); + * TWI_SlaveInitializeModule(&slave, TWI_SLAVE_ADDR, + * TWI_SLAVE_INTLVL_MED_gc); + * \endcode + * + * We zero out the receive buffer in the slave handle: + * \code + * for (i = 0; i < TWIS_SEND_BUFFER_SIZE; i++) { + * slave.receivedData[i] = 0; + * } + * \endcode + * + * And enable interrupts: + * \code + * cpu_irq_enable(); + * \endcode + * + * Finally, we write our packet through the master TWI module: + * \code + * twi_master_write(&TWI_MASTER, &packet); + * \endcode + * + * We wait for the slave to finish receiving: + * \code + * do { + * // Waiting + * } while(slave.result != TWIS_RESULT_OK); + * \endcode + * \note When the slave has finished receiving, the slave_process() + * function will copy the received data into our recv_data buffer, + * which now contains what was sent through the master. + * + */ + + + /** + * \page xmega_twi_xmegae XMEGA E TWI additions with Bridge and Fast Mode Plus + * + * XMEGA E TWI module provides two additionnnal features compare to regular + * XMEGA TWI module: + * - Fast Mode Plus communication speed + * - Bridge Mode + * + * The following use case will set up the TWI module to be used in in Fast Mode + * Plus together with bridge mode. + * This use case is similar to the regular XMEGA TWI initialization, it only + * differs by the activation of both Bridge and Fast Mode Plus mode. + * + * \subsection xmegae_twi_quickstart_use_case_example_code Example code + * + * \code + * #define TWI_MASTER TWIC + * #define TWI_MASTER_PORT PORTC + * #define TWI_SLAVE TWIC + * #define TWI_SPEED 1000000 + * #define TWI_MASTER_ADDR 0x50 + * #define TWI_SLAVE_ADDR 0x50 + * + * #define DATA_LENGTH 8 + * + * TWI_Slave_t slave; + * + * uint8_t data[DATA_LENGTH] = { + * 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f + * }; + * + * uint8_t recv_data[DATA_LENGTH] = { + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + * }; + * + * twi_options_t m_options = { + * .speed = TWI_SPEED, + * .chip = TWI_MASTER_ADDR, + * .speed_reg = TWI_BAUD(sysclk_get_cpu_hz(), TWI_SPEED) + * }; + * + * static void slave_process(void) { + * int i; + * + * for(i = 0; i < DATA_LENGTH; i++) { + * recv_data[i] = slave.receivedData[i]; + * } + * } + * + * ISR(TWIC_TWIS_vect) { + * TWI_SlaveInterruptHandler(&slave); + * } + * + * void send_and_recv_twi() + * { + * twi_package_t packet = { + * .addr_length = 0, + * .chip = TWI_SLAVE_ADDR, + * .buffer = (void *)data, + * .length = DATA_LENGTH, + * .no_wait = false + * }; + * + * uint8_t i; + * + * TWI_MASTER_PORT.PIN0CTRL = PORT_OPC_WIREDANDPULL_gc; + * TWI_MASTER_PORT.PIN1CTRL = PORT_OPC_WIREDANDPULL_gc; + * + * irq_initialize_vectors(); + * + * sysclk_enable_peripheral_clock(&TWI_MASTER); + * + * twi_bridge_enable(&TWI_MASTER); + * twi_fast_mode_enable(&TWI_MASTER); + * twi_slave_fast_mode_enable(&TWI_SLAVE); + * + * twi_master_init(&TWI_MASTER, &m_options); + * twi_master_enable(&TWI_MASTER); + * + * sysclk_enable_peripheral_clock(&TWI_SLAVE); + * TWI_SlaveInitializeDriver(&slave, &TWI_SLAVE, *slave_process); + * TWI_SlaveInitializeModule(&slave, TWI_SLAVE_ADDR, + * TWI_SLAVE_INTLVL_MED_gc); + * + * for (i = 0; i < TWIS_SEND_BUFFER_SIZE; i++) { + * slave.receivedData[i] = 0; + * } + * + * cpu_irq_enable(); + * + * twi_master_write(&TWI_MASTER, &packet); + * + * do { + * // Nothing + * } while(slave.result != TWIS_RESULT_OK); + * } + * \endcode + * + * \subsection xmegae_twi_quickstart_use_case_workflow Workflow + * We first create some definitions. TWI master and slave, speed, and + * addresses: + * \code + * #define TWI_MASTER TWIC + * #define TWI_MASTER_PORT PORTC + * #define TWI_SLAVE TWIC + * #define TWI_SPEED 1000000 + * #define TWI_MASTER_ADDR 0x50 + * #define TWI_SLAVE_ADDR 0x50 + * + * #define DATA_LENGTH 8 + * \endcode + * + * We create a handle to contain information about the slave module: + * \code + * TWI_Slave_t slave; + * \endcode + * + * We create two variables, one which contains data that will be transmitted, + * and one which will contain the received data: + * \code + * uint8_t data[DATA_LENGTH] = { + * 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f + * }; + * + * uint8_t recv_data[DATA_LENGTH] = { + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + * }; + * \endcode + * + * Options for the TWI module initialization procedure are given below: + * \code + * twi_options_t m_options = { + * .speed = TWI_SPEED, + * .chip = TWI_MASTER_ADDR, + * .speed_reg = TWI_BAUD(sysclk_get_cpu_hz(), TWI_SPEED) + * }; + * \endcode + * + * The TWI slave will fire an interrupt when it has received data, and the + * function below will be called, which will copy the data from the driver + * to our recv_data buffer: + * \code + * static void slave_process(void) { + * int i; + * + * for(i = 0; i < DATA_LENGTH; i++) { + * recv_data[i] = slave.receivedData[i]; + * } + * } + * \endcode + * + * Set up the interrupt handler: + * \code + * ISR(TWIC_TWIS_vect) { + * TWI_SlaveInterruptHandler(&slave); + * } + * \endcode + * + * We create a packet for the data that we will send to the slave TWI: + * \code + * twi_package_t packet = { + * .addr_length = 0, + * .chip = TWI_SLAVE_ADDR, + * .buffer = (void *)data, + * .length = DATA_LENGTH, + * .no_wait = false + * }; + * \endcode + * + * We need to set SDA/SCL pins for the master TWI to be wired and + * enable pull-up: + * \code + * TWI_MASTER_PORT.PIN0CTRL = PORT_OPC_WIREDANDPULL_gc; + * TWI_MASTER_PORT.PIN1CTRL = PORT_OPC_WIREDANDPULL_gc; + * \endcode + * + * We enable all interrupt levels: + * \code + * irq_initialize_vectors(); + * \endcode + * + * We enable the clock to the master module: + * \code + * sysclk_enable_peripheral_clock(&TWI_MASTER); + * \endcode + * + * We enable the global TWI bridge mode as well as the Fast Mode Plus + * communication speed for both master and slave: + * \code + * twi_bridge_enable(&TWI_MASTER); + * twi_fast_mode_enable(&TWI_MASTER); + * twi_slave_fast_mode_enable(&TWI_SLAVE); + * \endcode + * + * Initialize the master module with the options we described before: + * \code + * twi_master_init(&TWI_MASTER, &m_options); + * twi_master_enable(&TWI_MASTER); + * \endcode + * + * We do the same for the slave, using the slave portion of the driver, + * passing through the slave_process function, its address, and set medium + * interrupt level: + * \code + * sysclk_enable_peripheral_clock(&TWI_SLAVE); + * TWI_SlaveInitializeDriver(&slave, &TWI_SLAVE, *slave_process); + * TWI_SlaveInitializeModule(&slave, TWI_SLAVE_ADDR, + * TWI_SLAVE_INTLVL_MED_gc); + * \endcode + * + * We zero out the receive buffer in the slave handle: + * \code + * for (i = 0; i < TWIS_SEND_BUFFER_SIZE; i++) { + * slave.receivedData[i] = 0; + * } + * \endcode + * + * And enable interrupts: + * \code + * cpu_irq_enable(); + * \endcode + * + * Finally, we write our packet through the master TWI module: + * \code + * twi_master_write(&TWI_MASTER, &packet); + * \endcode + * + * We wait for the slave to finish receiving: + * \code + * do { + * // Waiting + * } while(slave.result != TWIS_RESULT_OK); + * \endcode + * \note When the slave has finished receiving, the slave_process() + * function will copy the received data into our recv_data buffer, + * which now contains what was sent through the master. + * + */ + + +#if XMEGA_E + +/*! \brief Enable bridge mode on TWIC. + * SDA and SCL are on PORTC for Master and on PORTD for slave + * + * \param twi Base address of the TWI instance. + */ +static inline void +twi_bridge_enable (TWI_t * twi) +{ + twi->CTRL |= TWI_BRIDGEEN_bm; +} + +/*! \brief Disable bridge mode on TWIC. + * + * \param twi Base address of the TWI instance. + */ +static inline void +twi_bridge_disable (TWI_t * twi) +{ + twi->CTRL &= (~TWI_BRIDGEEN_bm); +} + + +/*! \brief Enable Fast mode plus on TWIC (1MHz). + * FMPEN bit enables 1MHz on master and slave. + * In bridge mode, it enables only 1MHz on master. + * + * \param twi Base address of the TWI instance. + */ +static inline void +twi_fast_mode_enable (TWI_t * twi) +{ + twi->CTRL |= TWI_FMPEN_bm; +} + +/*! \brief Disable Fast mode plus on TWIC (1MHz). + * + * \param twi Base address of the TWI instance. + */ +static inline void +twi_fast_mode_disable (TWI_t * twi) +{ + twi->CTRL &= (~TWI_FMPEN_bm); +} + +/*! \brief Enable Fast mode plus for slave. + * If set in bridge mode, it enables 1MHz on slave. + * + * \param twi Base address of the TWI instance. + */ +static inline void +twi_slave_fast_mode_enable (TWI_t * twi) +{ + twi->CTRL |= TWI_SFMPEN_bm; +} + +/*! \brief Disable Fast mode plus for slave. + * If reset in bridge mode, it disables 1MHz on slave. + * + * \param twi Base address of the TWI instance. + */ +static inline void +twi_slave_fast_mode_disable (TWI_t * twi) +{ + twi->CTRL &= (~TWI_SFMPEN_bm); +} +#endif + +#endif // TWI_COMMON_H diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twim.c b/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twim.c new file mode 100644 index 00000000..314008a0 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twim.c @@ -0,0 +1,389 @@ +/** + * \file + * + * \brief XMEGA TWI master source file. + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + + +#include "twim.h" + + +/* Master Transfer Descriptor */ + +static struct { + TWI_t *bus; // Bus register interface + twi_package_t *pkg; // Bus message descriptor + int addr_count; // Bus transfer address data counter + unsigned int data_count; // Bus transfer payload data counter + bool read; // Bus transfer direction + bool locked; // Bus busy or unavailable + volatile status_code_t status; // Transfer status + +} transfer; + + +/** + * \internal + * + * \brief TWI Master Interrupt Vectors + * + * The TWI master interrupt request entry points are conditionally compiled + * for the TWI interfaces supported by the XMEGA MCU variant. All of these + * entry points call a common service function, twim_interrupt_handler(), + * to handle bus events. This handler uses the bus interface and message + * parameters specified in the global \c transfer structure. + */ +static void twim_interrupt_handler(void); + +#ifdef TWIC +ISR(TWIC_TWIM_vect) +{ + twim_interrupt_handler(); +} +#endif +#ifdef TWID +ISR(TWID_TWIM_vect) +{ + twim_interrupt_handler(); +} +#endif +#ifdef TWIE +ISR(TWIE_TWIM_vect) +{ + twim_interrupt_handler(); +} +#endif +#ifdef TWIF +ISR(TWIF_TWIM_vect) +{ + twim_interrupt_handler(); +} +#endif + +/** + * \internal + * + * \brief Test for an idle bus state. + * + * Software can determine the TWI master bus state (unknown, idle, owner, or + * busy) by reading the bus master status register: + * + * TWI_MASTER_BUSSTATE_UNKNOWN_gc Bus state is unknown. + * TWI_MASTER_BUSSTATE_IDLE_gc Bus state is idle. + * TWI_MASTER_BUSSTATE_OWNER_gc Bus state is owned by the master. + * TWI_MASTER_BUSSTATE_BUSY_gc Bus state is busy. + * + * \param twi Base address of the TWI (i.e. &TWI_t). + * + * \retval true The bus is currently idle. + * \retval false The bus is currently busy. + */ +static inline bool twim_idle(const TWI_t * twi) +{ + + return ((twi->MASTER.STATUS & TWI_MASTER_BUSSTATE_gm) + == TWI_MASTER_BUSSTATE_IDLE_gc); +} + +/** + * \internal + * + * \brief Get exclusive access to global TWI resources. + * + * Wait to acquire bus hardware interface and ISR variables. + * + * \param no_wait Set \c true to return instead of doing busy-wait (spin-lock). + * + * \return STATUS_OK if the bus is acquired, else ERR_BUSY. + */ +static inline status_code_t twim_acquire(bool no_wait) +{ + while (transfer.locked) { + + if (no_wait) { + return ERR_BUSY; + } + } + + irqflags_t const flags = cpu_irq_save(); + + transfer.locked = true; + transfer.status = OPERATION_IN_PROGRESS; + + cpu_irq_restore(flags); + + return STATUS_OK; +} + +/** + * \internal + * + * \brief Release exclusive access to global TWI resources. + * + * Release bus hardware interface and ISR variables previously locked by + * a call to \ref twim_acquire(). This function will busy-wait for + * pending driver operations to complete. + * + * \return status_code_t + * - STATUS_OK if the transfer completes + * - ERR_BUSY to indicate an unavailable bus + * - ERR_IO_ERROR to indicate a bus transaction error + * - ERR_NO_MEMORY to indicate buffer errors + * - ERR_PROTOCOL to indicate an unexpected bus state + */ +static inline status_code_t twim_release(void) +{ + /* First wait for the driver event handler to indicate something + * other than a transfer in-progress, then test the bus interface + * for an Idle bus state. + */ + while (OPERATION_IN_PROGRESS == transfer.status); + + while (!twim_idle(transfer.bus)) { + barrier(); + } + + status_code_t const status = transfer.status; + + transfer.locked = false; + + return status; +} + +/** + * \internal + * + * \brief TWI master write interrupt handler. + * + * Handles TWI transactions (master write) and responses to (N)ACK. + */ +static inline void twim_write_handler(void) +{ + TWI_t *const bus = transfer.bus; + twi_package_t *const pkg = transfer.pkg; + + if (transfer.addr_count < pkg->addr_length) { + + const uint8_t *const data = pkg->addr; + bus->MASTER.DATA = data[transfer.addr_count++]; + + } else if (transfer.data_count < pkg->length) { + + if (transfer.read) { + + /* Send repeated START condition (Address|R/W=1). */ + + bus->MASTER.ADDR |= 0x01; + + } else { + const uint8_t *const data = pkg->buffer; + bus->MASTER.DATA = data[transfer.data_count++]; + } + + } else { + + /* Send STOP condition to complete the transaction. */ + + bus->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc; + transfer.status = STATUS_OK; + } +} + +/** + * \internal + * + * \brief TWI master read interrupt handler. + * + * This is the master read interrupt handler that takes care of + * reading bytes from the TWI slave. + */ +static inline void twim_read_handler(void) +{ + TWI_t *const bus = transfer.bus; + twi_package_t *const pkg = transfer.pkg; + + if (transfer.data_count < pkg->length) { + + uint8_t *const data = pkg->buffer; + data[transfer.data_count++] = bus->MASTER.DATA; + + /* If there is more to read, issue ACK and start a byte read. + * Otherwise, issue NACK and STOP to complete the transaction. + */ + if (transfer.data_count < pkg->length) { + + bus->MASTER.CTRLC = TWI_MASTER_CMD_RECVTRANS_gc; + + } else { + + bus->MASTER.CTRLC = TWI_MASTER_ACKACT_bm | TWI_MASTER_CMD_STOP_gc; + transfer.status = STATUS_OK; + } + + } else { + + /* Issue STOP and buffer overflow condition. */ + + bus->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc; + transfer.status = ERR_NO_MEMORY; + } +} + +/** + * \internal + * + * \brief Common TWI master interrupt service routine. + * + * Check current status and calls the appropriate handler. + */ +static void twim_interrupt_handler(void) +{ + uint8_t const master_status = transfer.bus->MASTER.STATUS; + + if (master_status & TWI_MASTER_ARBLOST_bm) { + + transfer.bus->MASTER.STATUS = master_status | TWI_MASTER_ARBLOST_bm; + transfer.bus->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc; + transfer.status = ERR_BUSY; + + } else if ((master_status & TWI_MASTER_BUSERR_bm) || + (master_status & TWI_MASTER_RXACK_bm)) { + + transfer.bus->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc; + transfer.status = ERR_IO_ERROR; + + } else if (master_status & TWI_MASTER_WIF_bm) { + + twim_write_handler(); + + } else if (master_status & TWI_MASTER_RIF_bm) { + + twim_read_handler(); + + } else { + + transfer.status = ERR_PROTOCOL; + } +} + +/** + * \brief Initialize the twi master module + * + * \param twi Base address of the TWI (i.e. &TWIC). + * \param *opt Options for initializing the twi module + * (see \ref twi_options_t) + * \retval STATUS_OK Transaction is successful + * \retval ERR_INVALID_ARG Invalid arguments in \c opt. + */ +status_code_t twi_master_init(TWI_t * twi, + const twi_options_t * opt) +{ + uint8_t const ctrla = + CONF_TWIM_INTLVL | TWI_MASTER_RIEN_bm | TWI_MASTER_WIEN_bm | + TWI_MASTER_ENABLE_bm; + + twi->MASTER.BAUD = opt->speed_reg; + twi->MASTER.CTRLA = ctrla; + twi->MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc; + + transfer.locked = false; + transfer.status = STATUS_OK; + + /* Enable configured PMIC interrupt level. */ + + PMIC.CTRL |= CONF_PMIC_INTLVL; + + cpu_irq_enable(); + + return STATUS_OK; +} + +/** + * \brief Perform a TWI master write or read transfer. + * + * This function is a TWI Master write or read transaction. + * + * \param twi Base address of the TWI (i.e. &TWI_t). + * \param package Package information and data + * (see \ref twi_package_t) + * \param read Selects the transfer direction + * + * \return status_code_t + * - STATUS_OK if the transfer completes + * - ERR_BUSY to indicate an unavailable bus + * - ERR_IO_ERROR to indicate a bus transaction error + * - ERR_NO_MEMORY to indicate buffer errors + * - ERR_PROTOCOL to indicate an unexpected bus state + * - ERR_INVALID_ARG to indicate invalid arguments. + */ +status_code_t twi_master_transfer(TWI_t * twi, + const twi_package_t * package, + bool read) +{ + /* Do a sanity check on the arguments. */ + + if ((twi == NULL) || (package == NULL)) { + return ERR_INVALID_ARG; + } + + /* Initiate a transaction when the bus is ready. */ + + status_code_t status = twim_acquire(package->no_wait); + + if (STATUS_OK == status) { + transfer.bus = (TWI_t *) twi; + transfer.pkg = (twi_package_t *) package; + transfer.addr_count = 0; + transfer.data_count = 0; + transfer.read = read; + + uint8_t const chip = (package->chip) << 1; + + if (package->addr_length || (false == read)) { + transfer.bus->MASTER.ADDR = chip; + } else if (read) { + transfer.bus->MASTER.ADDR = chip | 0x01; + } + + status = twim_release(); + } + + return status; +} diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twim.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twim.h new file mode 100644 index 00000000..2f4da266 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twim.h @@ -0,0 +1,168 @@ +/** + * \file + * + * \brief TWI driver for AVR. + * + * This file defines a useful set of functions for the TWI interface on AVR + * devices. + * + * Copyright (c) 2010-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _TWIM_H_ +#define _TWIM_H_ + +/** + * \defgroup group_xmega_drivers_twi_twim TWI Master + * + * \ingroup group_xmega_drivers_twi + * + * \{ + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#include +#include + +#include "conf_twim.h" +#include "twi_common.h" + + +/*! \brief Error Codes for the Module + * + * \deprecated + * This definition is provided for compatibility with existing ASF example + * applications. This module uses the \ref status_code_t values that will + * replace module-specific error codes in ASF drivers. + */ +#define TWI_SUCCESS (STATUS_OK) + + +/*! Baud register setting calculation. Formula described in datasheet. */ +#define TWI_BAUD(F_SYS, F_TWI) ((F_SYS / (2 * F_TWI)) - 5) + + +/*! \brief Initialize the twi master module + * + * \param twi Base address of the TWI (i.e. &TWIC). + * \param *opt Options for initializing the twi module + * (see \ref twi_options_t) + * \retval STATUS_OK Transaction is successful + * \retval ERR_INVALID_ARG Invalid arguments in \c opt. + */ + status_code_t twi_master_init (TWI_t * twi, const twi_options_t * opt); + +/*! \brief Perform a TWI master write or read transfer. + * + * This function is a TWI Master write or read transaction. + * + * \param twi Base address of the TWI (i.e. &TWI_t). + * \param package Package information and data + * (see \ref twi_package_t) + * \param read Selects the transfer direction + * + * \return status_code_t + * - STATUS_OK if the transfer completes + * - ERR_BUSY to indicate an unavailable bus + * - ERR_IO_ERROR to indicate a bus transaction error + * - ERR_NO_MEMORY to indicate buffer errors + * - ERR_PROTOCOL to indicate an unexpected bus state + * - ERR_INVALID_ARG to indicate invalid arguments. + */ + status_code_t twi_master_transfer (TWI_t * twi, + const twi_package_t * package, + bool read); + +/*! \brief Read multiple bytes from a TWI compatible slave device + * + * \param twi Base address of the TWI (i.e. &TWI_t). + * \param package Package information and data + * (see \ref twi_package_t) + * \return STATUS_OK If all bytes were read, error code otherwise + */ + static inline status_code_t twi_master_read (TWI_t * twi, + const twi_package_t * package) + { + return twi_master_transfer (twi, package, true); + } + +/*! \brief Write multiple bytes to a TWI compatible slave device + * + * \param twi Base address of the TWI (i.e. &TWI_t). + * \param package Package information and data + * (see \ref twi_package_t) + * \return STATUS_OK If all bytes were written, error code otherwise + */ + static inline status_code_t twi_master_write (TWI_t * twi, + const twi_package_t * package) + { + return twi_master_transfer (twi, package, false); + } + +/*! \brief Enable Master Mode of the TWI. + * + * \param twi Base address of the TWI instance. + */ + static inline void twi_master_enable (TWI_t * twi) + { + twi->MASTER.CTRLA |= TWI_MASTER_ENABLE_bm; + } + +/*! \brief Disable Master Mode of the TWI. + * + * \param twi Base address of the TWI instance. + */ + static inline void twi_master_disable (TWI_t * twi) + { + twi->MASTER.CTRLA &= (~TWI_MASTER_ENABLE_bm); + } + + +#ifdef __cplusplus +} +#endif + +/** + * \} + */ + +#endif // _TWIM_H_ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twis.c b/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twis.c new file mode 100644 index 00000000..6f7ce493 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twis.c @@ -0,0 +1,330 @@ +/** + * \file ********************************************************************* + * + * \brief + * XMEGA TWI slave driver source file. + * + * This file contains the function implementations the XMEGA TWI slave + * driver. + * + * The driver is not intended for size and/or speed critical code, since + * most functions are just a few lines of code, and the function call + * overhead would decrease code performance. The driver is intended for + * rapid prototyping and documentation purposes for getting started with + * the XMEGA TWI slave module. + * + * For size and/or speed critical code, it is recommended to copy the + * function contents directly into your application instead of making + * a function call. + * + * Several functions use the following construct: + * "some_register = ... | (some_parameter ? SOME_BIT_bm : 0) | ..." + * Although the use of the ternary operator ( if ? then : else ) is + * discouraged, in some occasions the operator makes it possible to write + * pretty clean and neat code. In this driver, the construct is used to + * set or not set a configuration bit based on a boolean input parameter, + * such as the "some_parameter" in the example above. + * + * \par Application note: + * AVR1308: Using the XMEGA TWI + * + * \par Documentation + * For comprehensive code documentation, supported compilers, compiler + * settings and supported devices see readme.html + * + * Atmel Corporation: http://www.atmel.com \n + * + * $Revision: 2660 $ + * $Date: 2009-08-11 12:28:58 +0200 (Tue, 11 Aug 2009) $ \n + * + * Copyright (c) 2008 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + *****************************************************************************/ + +#include "twis.h" + + +/*! \brief Initalizes TWI slave driver structure. + * + * Initialize the instance of the TWI Slave and set the appropriate values. + * + * \param twi The TWI_Slave_t struct instance. + * \param module Pointer to the TWI module. + * \param processDataFunction Pointer to the function that handles incoming data. + */ +void TWI_SlaveInitializeDriver(TWI_Slave_t * twi, + TWI_t * module, + void (*processDataFunction) (void)) +{ + twi->interface = module; + twi->Process_Data = processDataFunction; + twi->bytesReceived = 0; + twi->bytesSent = 0; + twi->status = TWIS_STATUS_READY; + twi->result = TWIS_RESULT_UNKNOWN; + twi->abort = false; +} + + +/*! \brief Initialize the TWI module. + * + * Enables interrupts on address recognition and data available. + * Remember to enable interrupts globally from the main application. + * + * \param twi The TWI_Slave_t struct instance. + * \param address Slave address for this module. + * \param intLevel Interrupt level for the TWI slave interrupt handler. + */ +void TWI_SlaveInitializeModule(TWI_Slave_t * twi, + uint8_t address, + TWI_SLAVE_INTLVL_t intLevel) +{ + twi->interface->SLAVE.CTRLA = + intLevel | TWI_SLAVE_DIEN_bm | TWI_SLAVE_APIEN_bm | + TWI_SLAVE_ENABLE_bm; + twi->interface->SLAVE.ADDR = (address << 1); +} + + +/*! \brief Common TWI slave interrupt service routine. + * + * Handles all TWI transactions and responses to address match, data reception, + * data transmission, bus error and data collision. + * + * \param twi The TWI_Slave_t struct instance. + */ +void TWI_SlaveInterruptHandler(TWI_Slave_t * twi) +{ + uint8_t currentStatus = twi->interface->SLAVE.STATUS; + + /* If bus error. */ + if (currentStatus & TWI_SLAVE_BUSERR_bm) { + twi->bytesReceived = 0; + twi->bytesSent = 0; + twi->result = TWIS_RESULT_BUS_ERROR; + twi->status = TWIS_STATUS_READY; + } + + /* If transmit collision. */ + else if (currentStatus & TWI_SLAVE_COLL_bm) { + twi->bytesReceived = 0; + twi->bytesSent = 0; + twi->result = TWIS_RESULT_TRANSMIT_COLLISION; + twi->status = TWIS_STATUS_READY; + } + + /* If address match. */ + else if ((currentStatus & TWI_SLAVE_APIF_bm) && + (currentStatus & TWI_SLAVE_AP_bm)) { + + TWI_SlaveAddressMatchHandler(twi); + } + + /* If stop (only enabled through slave read transaction). */ + else if (currentStatus & TWI_SLAVE_APIF_bm) { + TWI_SlaveStopHandler(twi); + } + + /* If data interrupt. */ + else if (currentStatus & TWI_SLAVE_DIF_bm) { + TWI_SlaveDataHandler(twi); + } + + /* If unexpected state. */ + else { + TWI_SlaveTransactionFinished(twi, TWIS_RESULT_FAIL); + } +} + +/*! \brief TWI address match interrupt handler. + * + * Prepares TWI module for transaction when an address match occurs. + * + * \param twi The TWI_Slave_t struct instance. + */ +void TWI_SlaveAddressMatchHandler(TWI_Slave_t * twi) +{ + /* If application signalling need to abort (error occured). */ + if (twi->abort) { + twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc; + TWI_SlaveTransactionFinished(twi, TWIS_RESULT_ABORTED); + twi->abort = false; + } else { + twi->status = TWIS_STATUS_BUSY; + twi->result = TWIS_RESULT_UNKNOWN; + + /* Disable stop interrupt. */ + uint8_t currentCtrlA = twi->interface->SLAVE.CTRLA; + twi->interface->SLAVE.CTRLA = currentCtrlA & ~TWI_SLAVE_PIEN_bm; + + twi->bytesReceived = 0; + twi->bytesSent = 0; + + /* Send ACK, wait for data interrupt. */ + twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; + } +} + + +/*! \brief TWI stop condition interrupt handler. + * + * \param twi The TWI_Slave_t struct instance. + */ +void TWI_SlaveStopHandler(TWI_Slave_t * twi) +{ + /* Disable stop interrupt. */ + uint8_t currentCtrlA = twi->interface->SLAVE.CTRLA; + twi->interface->SLAVE.CTRLA = currentCtrlA & ~TWI_SLAVE_PIEN_bm; + + /* Clear APIF, according to flowchart don't ACK or NACK */ + uint8_t currentStatus = twi->interface->SLAVE.STATUS; + twi->interface->SLAVE.STATUS = currentStatus | TWI_SLAVE_APIF_bm; + + TWI_SlaveTransactionFinished(twi, TWIS_RESULT_OK); + +} + + +/*! \brief TWI data interrupt handler. + * + * Calls the appropriate slave read or write handler. + * + * \param twi The TWI_Slave_t struct instance. + */ +void TWI_SlaveDataHandler(TWI_Slave_t * twi) +{ + if (twi->interface->SLAVE.STATUS & TWI_SLAVE_DIR_bm) { + TWI_SlaveWriteHandler(twi); + } else { + TWI_SlaveReadHandler(twi); + } +} + + +/*! \brief TWI slave read interrupt handler. + * + * Handles TWI slave read transactions and responses. + * + * \param twi The TWI_Slave_t struct instance. + */ +void TWI_SlaveReadHandler(TWI_Slave_t * twi) +{ + /* Enable stop interrupt. */ + uint8_t currentCtrlA = twi->interface->SLAVE.CTRLA; + twi->interface->SLAVE.CTRLA = currentCtrlA | TWI_SLAVE_PIEN_bm; + + /* If free space in buffer. */ + if (twi->bytesReceived < TWIS_RECEIVE_BUFFER_SIZE) { + /* Fetch data */ + uint8_t data = twi->interface->SLAVE.DATA; + twi->receivedData[twi->bytesReceived] = data; + + /* Process data. */ + twi->Process_Data(); + + twi->bytesReceived++; + + /* If application signalling need to abort (error occured), + * complete transaction and wait for next START. Otherwise + * send ACK and wait for data interrupt. + */ + if (twi->abort) { + twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc; + TWI_SlaveTransactionFinished(twi, TWIS_RESULT_ABORTED); + twi->abort = false; + } else { + twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; + } + } + /* If buffer overflow, send NACK and wait for next START. Set + * result buffer overflow. + */ + else { + twi->interface->SLAVE.CTRLB = + TWI_SLAVE_ACKACT_bm | TWI_SLAVE_CMD_COMPTRANS_gc; + TWI_SlaveTransactionFinished(twi, TWIS_RESULT_BUFFER_OVERFLOW); + } +} + + +/*! \brief TWI slave write interrupt handler. + * + * Handles TWI slave write transactions and responses. + * + * \param twi The TWI_Slave_t struct instance. + */ +void TWI_SlaveWriteHandler(TWI_Slave_t * twi) +{ + /* If NACK, slave write transaction finished. */ + if ((twi->bytesSent > 0) && + (twi->interface->SLAVE.STATUS & TWI_SLAVE_RXACK_bm)) { + + twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc; + TWI_SlaveTransactionFinished(twi, TWIS_RESULT_OK); + } + /* If ACK, master expects more data. */ + else { + if (twi->bytesSent < TWIS_SEND_BUFFER_SIZE) { + uint8_t data = twi->sendData[twi->bytesSent]; + twi->interface->SLAVE.DATA = data; + twi->bytesSent++; + + /* Send data, wait for data interrupt. */ + twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; + } + /* If buffer overflow. */ + else { + twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc; + TWI_SlaveTransactionFinished(twi, TWIS_RESULT_BUFFER_OVERFLOW); + } + } +} + + +/*! \brief TWI transaction finished function. + * + * Prepares module for new transaction. + * + * \param twi The TWI_Slave_t struct instance. + * \param result The result of the transaction. + */ +void TWI_SlaveTransactionFinished(TWI_Slave_t * twi, + uint8_t result) +{ + twi->result = result; + twi->status = TWIS_STATUS_READY; +} diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twis.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twis.h new file mode 100644 index 00000000..9b1d812d --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/twi/twis.h @@ -0,0 +1,179 @@ +/** + * \file ********************************************************************* + * + * \brief XMEGA TWI slave driver header file. + * + * This file contains the function prototypes and enumerator definitions + * for various configuration parameters for the XMEGA TWI slave driver. + * + * The driver is not intended for size and/or speed critical code, since + * most functions are just a few lines of code, and the function call + * overhead would decrease code performance. The driver is intended for + * rapid prototyping and documentation purposes for getting started with + * the XMEGA TWI slave module. + * + * For size and/or speed critical code, it is recommended to copy the + * function contents directly into your application instead of making + * a function call. + * + * \par Application note: + * AVR1307: Using the XMEGA TWI + * + * \par Documentation + * For comprehensive code documentation, supported compilers, compiler + * settings and supported devices see readme.html + * + * Atmel Corporation: http://www.atmel.com \n + * + * $Revision: 1569 $ + * $Date: 2008-04-22 13:03:43 +0200 (Tue, 22 Apr 2008) $ \n + * + * Copyright (c) 2008 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + *****************************************************************************/ +#ifndef TWIS_H +#define TWIS_H + +/** + * \defgroup group_xmega_drivers_twi_twis TWI Slave + * + * \ingroup group_xmega_drivers_twi + * + * \{ + */ + +#include "compiler.h" +#include "twi_common.h" + +/*! Baud register setting calculation. Formula described in datasheet. */ +#define TWI_BAUD(F_SYS, F_TWI) ((F_SYS / (2 * F_TWI)) - 5) + +/* Transaction status defines.*/ +#define TWIS_STATUS_READY 0 +#define TWIS_STATUS_BUSY 1 + +/* Transaction result enumeration */ +typedef enum TWIS_RESULT_enum +{ + TWIS_RESULT_UNKNOWN = (0x00 << 0), + TWIS_RESULT_OK = (0x01 << 0), + TWIS_RESULT_BUFFER_OVERFLOW = (0x02 << 0), + TWIS_RESULT_TRANSMIT_COLLISION = (0x03 << 0), + TWIS_RESULT_BUS_ERROR = (0x04 << 0), + TWIS_RESULT_FAIL = (0x05 << 0), + TWIS_RESULT_ABORTED = (0x06 << 0), +} +TWIS_RESULT_t; + +/* Buffer size defines. */ +#define TWIS_RECEIVE_BUFFER_SIZE 8 +#define TWIS_SEND_BUFFER_SIZE 8 + + + +/*! \brief TWI slave driver struct. + * + * TWI slave struct. Holds pointer to TWI module and data processing routine, + * buffers and necessary variables. + */ +typedef struct TWI_Slave +{ + TWI_t *interface; /*!< Pointer to what interface to use */ + void (*Process_Data) (void); /*!< Pointer to process data function */ + register8_t receivedData[TWIS_RECEIVE_BUFFER_SIZE]; /*!< Read data */ + register8_t sendData[TWIS_SEND_BUFFER_SIZE]; /*!< Data to write */ + register8_t bytesReceived; /*!< Number of bytes received */ + register8_t bytesSent; /*!< Number of bytes sent */ + register8_t status; /*!< Status of transaction */ + register8_t result; /*!< Result of transaction */ + bool abort; /*!< Strobe to abort */ +} +TWI_Slave_t; + + +void TWI_SlaveInitializeDriver (TWI_Slave_t * twi, + TWI_t * module, + void (*processDataFunction) (void)); + +void TWI_SlaveInitializeModule (TWI_Slave_t * twi, + uint8_t address, TWI_SLAVE_INTLVL_t intLevel); + +void TWI_SlaveInterruptHandler (TWI_Slave_t * twi); +void TWI_SlaveAddressMatchHandler (TWI_Slave_t * twi); +void TWI_SlaveStopHandler (TWI_Slave_t * twi); +void TWI_SlaveDataHandler (TWI_Slave_t * twi); +void TWI_SlaveReadHandler (TWI_Slave_t * twi); +void TWI_SlaveWriteHandler (TWI_Slave_t * twi); +void TWI_SlaveTransactionFinished (TWI_Slave_t * twi, uint8_t result); + + +/*! TWI slave interrupt service routine. + * + * Interrupt service routine for the TWI slave. Copy the interrupt vector + * into your code if needed. + * + ISR(TWIC_TWIS_vect) + { + TWI_SlaveInterruptHandler(&twiSlaveC); + } + * + */ +/*! \brief Enable Slave Mode of the TWI. + * + * \param twi Base address of the TWI instance. + */ +static inline void +twi_slave_enable (TWI_t * twi) +{ + twi->SLAVE.CTRLA |= TWI_SLAVE_ENABLE_bm; +} + +/*! \brief Disable Slave Mode of the TWI. + * + * \param twi Base address of the TWI instance. + */ +static inline void +twi_slave_disable (TWI_t * twi) +{ + twi->SLAVE.CTRLA &= (~TWI_SLAVE_ENABLE_bm); +} + +/** + * \} + */ + +#endif /* TWIS_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/usart/usart.c b/bacnet-stack/ports/xplained/ASF/xmega/drivers/usart/usart.c new file mode 100644 index 00000000..50c763ab --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/usart/usart.c @@ -0,0 +1,464 @@ +/** + * \file + * + * \brief USART driver for AVR XMEGA. + * + * Copyright (c) 2009-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include +#include "compiler.h" +#include "usart.h" +#include "sysclk.h" +#include "ioport.h" +#include "status_codes.h" + +/* + * Fix XMEGA header files + * USART.CTRLC bit masks and bit positions + */ +#ifndef USART_UCPHA_bm +# define USART_UCPHA_bm 0x02 +#endif +#ifndef USART_DORD_bm +# define USART_DORD_bm 0x04 +#endif + +/** + * \brief Initialize USART in RS232 mode. + * + * This function initializes the USART module in RS232 mode using the + * usart_rs232_options_t configuration structure and CPU frequency. + * + * \param usart The USART module. + * \param opt The RS232 configuration option. + * + * \retval true if the initialization was successfull + * \retval false if the initialization failed (error in baud rate calculation) + */ +bool usart_init_rs232(USART_t *usart, const usart_rs232_options_t *opt) +{ + bool result; + sysclk_enable_peripheral_clock(usart); + usart_set_mode(usart, USART_CMODE_ASYNCHRONOUS_gc); + usart_format_set(usart, opt->charlength, opt->paritytype, + opt->stopbits); + result = usart_set_baudrate(usart, opt->baudrate, sysclk_get_per_hz()); + usart_tx_enable(usart); + usart_rx_enable(usart); + + return result; +} + +/** + * \brief Initialize USART in SPI master mode. + * + * This function initializes the USART module in SPI master mode using the + * usart_spi_options_t configuration structure and CPU frequency. + * + * \param usart The USART module. + * \param opt The RS232 configuration option. + */ +void usart_init_spi(USART_t *usart, const usart_spi_options_t *opt) +{ + ioport_pin_t sck_pin; + bool invert_sck; + + sysclk_enable_peripheral_clock(usart); + + usart_rx_disable(usart); + + /* configure Clock polarity using INVEN bit of the correct SCK I/O port **/ + invert_sck = (opt->spimode == 2) || (opt->spimode == 3); + UNUSED(invert_sck); + +#ifdef USARTC0 + if ((uint16_t)usart == (uint16_t)&USARTC0) { +# ifdef PORT_USART0_bm + if (PORTC.REMAP & PORT_USART0_bm) { + sck_pin = IOPORT_CREATE_PIN(PORTC, 5); + } else { + sck_pin = IOPORT_CREATE_PIN(PORTC, 1); + } +# else + sck_pin = IOPORT_CREATE_PIN(PORTC, 1); +# endif + } +#endif +#ifdef USARTC1 + if ((uint16_t)usart == (uint16_t)&USARTC1) { + sck_pin = IOPORT_CREATE_PIN(PORTC, 5); + } +#endif +#ifdef USARTD0 + if ((uint16_t)usart == (uint16_t)&USARTD0) { +# ifdef PORT_USART0_bm + if (PORTD.REMAP & PORT_USART0_bm) { + sck_pin = IOPORT_CREATE_PIN(PORTD, 5); + } else { + sck_pin = IOPORT_CREATE_PIN(PORTD, 1); + } +# else + sck_pin = IOPORT_CREATE_PIN(PORTD, 1); +# endif + } +#endif +#ifdef USARTD1 + if ((uint16_t)usart == (uint16_t)&USARTD1) { + sck_pin = IOPORT_CREATE_PIN(PORTD, 5); + } +#endif +#ifdef USARTE0 + if ((uint16_t)usart == (uint16_t)&USARTE0) { +# ifdef PORT_USART0_bm + if(PORTE.REMAP & PORT_USART0_bm) { + sck_pin = IOPORT_CREATE_PIN(PORTE, 5); + } else { + sck_pin = IOPORT_CREATE_PIN(PORTE, 1); + } +# else + sck_pin = IOPORT_CREATE_PIN(PORTE, 1); +# endif + } +#endif +#ifdef USARTE1 + if ((uint16_t)usart == (uint16_t)&USARTE1) { + sck_pin = IOPORT_CREATE_PIN(PORTE, 5); + } +#endif +#ifdef USARTF0 + if ((uint16_t)usart == (uint16_t)&USARTF0) { +# ifdef PORT_USART0_bm + if(PORTF.REMAP & PORT_USART0_bm) { + sck_pin = IOPORT_CREATE_PIN(PORTF, 5); + } else { + sck_pin = IOPORT_CREATE_PIN(PORTF, 1); + } +# else + sck_pin = IOPORT_CREATE_PIN(PORTF, 1); +# endif + } +#endif +#ifdef USARTF1 + if ((uint16_t)usart == (uint16_t)&USARTF1) { + sck_pin = IOPORT_CREATE_PIN(PORTF, 5); + } +#endif + + /* Configure the USART output pin */ + ioport_set_pin_dir(sck_pin, IOPORT_DIR_OUTPUT); + ioport_set_pin_mode(sck_pin, + IOPORT_MODE_TOTEM | (invert_sck? IOPORT_MODE_INVERT_PIN : 0)); + ioport_set_pin_level(sck_pin, IOPORT_PIN_LEVEL_HIGH); + + usart_set_mode(usart, USART_CMODE_MSPI_gc); + + if (opt->spimode == 1 || opt->spimode == 3) { + usart->CTRLC |= USART_UCPHA_bm; + } else { + usart->CTRLC &= ~USART_UCPHA_bm; + } + if (opt->data_order) { + (usart)->CTRLC |= USART_DORD_bm; + } else { + (usart)->CTRLC &= ~USART_DORD_bm; + } + + usart_spi_set_baudrate(usart, opt->baudrate, sysclk_get_per_hz()); + usart_tx_enable(usart); + usart_rx_enable(usart); +} + +/** + * \brief Send a data with the USART module + * + * This function outputs a data using the USART module. + * + * \param usart The USART module. + * \param c The data to send. + * + * \return STATUS_OK + */ +status_code_t usart_putchar(USART_t *usart, uint8_t c) +{ + while (usart_data_register_is_empty(usart) == false) { + } + + (usart)->DATA = c; + return STATUS_OK; +} + +/** + * \brief Receive a data with the USART module + * + * This function returns the received data from the USART module. + * + * \param usart The USART module. + * + * \return The received data. + */ +uint8_t usart_getchar(USART_t *usart) +{ + while (usart_rx_is_complete(usart) == false) { + } + + return ((uint8_t)(usart)->DATA); +} + +/** + * \brief Get the offset for lookup in the baudrate table + * + * \param baud The requested baudrate + * + * \return The baudrate offset in PROGMEM table + * \retval USART_BAUD_UNDEFINED for baudrates not in lookup table + */ +static uint8_t usart_get_baud_offset(uint32_t baud) +{ + switch (baud) { + case 1200: + return (uint8_t)USART_BAUD_1200; + + case 2400: + return (uint8_t)USART_BAUD_2400; + + case 4800: + return (uint8_t)USART_BAUD_4800; + + case 9600: + return (uint8_t)USART_BAUD_9600; + + case 19200: + return (uint8_t)USART_BAUD_19200; + + case 38400: + return (uint8_t)USART_BAUD_38400; + + case 57600: + return (uint8_t)USART_BAUD_57600; + + default: + return (uint8_t)USART_BAUD_UNDEFINED; + } +} + +/** + * \brief Set the baudrate by setting the BSEL and BSCALE values in the USART + * + * This function sets the selected BSEL and BSCALE value in the BAUDCTRL + * registers with BSCALE 0. For calculation options, see table 21-1 in XMEGA A + * manual. + * + * \param usart The USART module. + * \param bsel Calculated BSEL value. + * \param bscale Calculated BSEL value. + * + */ +void usart_set_bsel_bscale_value(USART_t *usart, uint16_t bsel, uint8_t bscale) +{ + (usart)->BAUDCTRLA = (uint8_t)(bsel); + (usart)->BAUDCTRLB = (uint8_t)(((bsel >> 8) & 0X0F) | (bscale << 4)); +} + +/** + * \brief Set the baudrate using precalculated BAUDCTRL values from PROGMEM + * + * \note This function only works for cpu_hz 2Mhz or 32Mhz and baudrate values + * 1200, 2400, 4800, 9600, 19200, 38400 and 57600. + * + * \param usart The USART module. + * \param baud The baudrate. + * \param cpu_hz The CPU frequency. + * + */ +void usart_set_baudrate_precalculated(USART_t *usart, uint32_t baud, + uint32_t cpu_hz) +{ + uint8_t baud_offset; + uint16_t baudctrl = 0; + + baud_offset = usart_get_baud_offset(baud); + + if (cpu_hz == 2000000UL) { + baudctrl = PROGMEM_READ_WORD(baudctrl_2mhz + baud_offset); + } else if (cpu_hz == 32000000UL) { + baudctrl = PROGMEM_READ_WORD(baudctrl_32mhz + baud_offset); + } else { + /* Error, system clock speed or USART baud rate is not supported + * by the look-up table */ + Assert(false); + } + + if (baud_offset != USART_BAUD_UNDEFINED) { + (usart)->BAUDCTRLB = (uint8_t)((uint16_t)baudctrl); + (usart)->BAUDCTRLA = (uint8_t)((uint16_t)baudctrl >> 8); + } +} + +/** + * \brief Set the baudrate value in the USART module + * + * This function sets the baudrate register with scaling regarding the CPU + * frequency and makes sure the baud rate is supported by the hardware. + * The function can be used if you don't want to calculate the settings + * yourself or changes to baudrate at runtime is required. + * + * \param usart The USART module. + * \param baud The baudrate. + * \param cpu_hz The CPU frequency. + * + * \retval true if the hardware supports the baud rate + * \retval false if the hardware does not support the baud rate (i.e. it's + * either too high or too low.) + */ +bool usart_set_baudrate(USART_t *usart, uint32_t baud, uint32_t cpu_hz) +{ + int8_t exp; + uint32_t div; + uint32_t limit; + uint32_t ratio; + uint32_t min_rate; + uint32_t max_rate; + + /* + * Check if the hardware supports the given baud rate + */ + /* 8 = (2^0) * 8 * (2^0) = (2^BSCALE_MIN) * 8 * (BSEL_MIN) */ + max_rate = cpu_hz / 8; + /* 4194304 = (2^7) * 8 * (2^12) = (2^BSCALE_MAX) * 8 * (BSEL_MAX+1) */ + min_rate = cpu_hz / 4194304; + + if (!((usart)->CTRLB & USART_CLK2X_bm)) { + max_rate /= 2; + min_rate /= 2; + } + + if ((baud > max_rate) || (baud < min_rate)) { + return false; + } + + /* Check if double speed is enabled. */ + if (!((usart)->CTRLB & USART_CLK2X_bm)) { + baud *= 2; + } + + /* Find the lowest possible exponent. */ + limit = 0xfffU >> 4; + ratio = cpu_hz / baud; + + for (exp = -7; exp < 7; exp++) { + if (ratio < limit) { + break; + } + + limit <<= 1; + + if (exp < -3) { + limit |= 1; + } + } + + /* + * Depending on the value of exp, scale either the input frequency or + * the target baud rate. By always scaling upwards, we never introduce + * any additional inaccuracy. + * + * We are including the final divide-by-8 (aka. right-shift-by-3) in + * this operation as it ensures that we never exceeed 2**32 at any + * point. + * + * The formula for calculating BSEL is slightly different when exp is + * negative than it is when exp is positive. + */ + if (exp < 0) { + /* We are supposed to subtract 1, then apply BSCALE. We want to + * apply BSCALE first, so we need to turn everything inside the + * parenthesis into a single fractional expression. + */ + cpu_hz -= 8 * baud; + + /* If we end up with a left-shift after taking the final + * divide-by-8 into account, do the shift before the divide. + * Otherwise, left-shift the denominator instead (effectively + * resulting in an overall right shift.) + */ + if (exp <= -3) { + div = ((cpu_hz << (-exp - 3)) + baud / 2) / baud; + } else { + baud <<= exp + 3; + div = (cpu_hz + baud / 2) / baud; + } + } else { + /* We will always do a right shift in this case, but we need to + * shift three extra positions because of the divide-by-8. + */ + baud <<= exp + 3; + div = (cpu_hz + baud / 2) / baud - 1; + } + + (usart)->BAUDCTRLB = (uint8_t)(((div >> 8) & 0X0F) | (exp << 4)); + (usart)->BAUDCTRLA = (uint8_t)div; + + return true; +} + +/** + * \brief Set the baudrate value in the USART_SPI module + * + * This function sets the baudrate register regarding the CPU frequency. + * + * \param usart The USART(SPI) module. + * \param baud The baudrate. + * \param cpu_hz The CPU frequency. + */ +void usart_spi_set_baudrate(USART_t *usart, uint32_t baud, uint32_t cpu_hz) +{ + uint16_t bsel_value; + + /* Check if baudrate is less than the maximim limit specified in + * datasheet */ + if (baud < (cpu_hz / 2)) { + bsel_value = (cpu_hz / (baud * 2)) - 1; + } else { + /* If baudrate is not within the specfication in datasheet, + * assign maximum baudrate possible for the current CPU frequency */ + bsel_value = 0; + } + + (usart)->BAUDCTRLB = (uint8_t)((~USART_BSCALE_gm) & (bsel_value >> 8)); + (usart)->BAUDCTRLA = (uint8_t)(bsel_value); +} diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/usart/usart.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/usart/usart.h new file mode 100644 index 00000000..0005d81d --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/usart/usart.h @@ -0,0 +1,556 @@ +/** + * \file + * + * \brief USART driver for AVR XMEGA. + * + * This file contains basic functions for the AVR XMEGA USART, with support for all + * modes, settings and clock speeds. + * + * Copyright (c) 2009-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _USART_H_ +#define _USART_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "compiler.h" +#include "pmic.h" +#include "status_codes.h" + +/** + * \defgroup usart_group USART module (USART) + * + * See \ref xmega_usart_quickstart. + * + * This is a driver for configuring, enabling, disabling and use of the on-chip + * USART. + * + * \section dependencies Dependencies + * + * The USART module depends on the following modules: + * - \ref sysclk_group for peripheral clock control. + * - \ref port_driver_group for peripheral io port control. + * + * @{ + */ + +//! Offset in lookup table for baudrate 1200 +#define USART_BAUD_1200 0x00 +//! Offset in lookup table for baudrate 2400 +#define USART_BAUD_2400 0x01 +//! Offset in lookup table for baudrate 4800 +#define USART_BAUD_4800 0x02 +//! Offset in lookup table for baudrate 9600 +#define USART_BAUD_9600 0x03 +//! Offset in lookup table for baudrate 19200 +#define USART_BAUD_19200 0x04 +//! Offset in lookup table for baudrate 38400 +#define USART_BAUD_38400 0x05 +//! Offset in lookup table for baudrate 57600 +#define USART_BAUD_57600 0x06 +//! Baudrate not in lookup table +#define USART_BAUD_UNDEFINED 0xFF + +//! Lookup table containing baudctrl values for CPU frequency 2 Mhz +static PROGMEM_DECLARE(uint16_t, baudctrl_2mhz[]) = { + 0xE5BC, // Baud: 1200 + 0xC5AC, // Baud: 2400 + 0x859C, // Baud: 4800 + 0x0396, // Baud: 9600 + 0xC192, // Baud: 19200 + 0x2191, // Baud: 38400 + 0x9690, // Baud: 57600 +}; + +//! Lookup table containing baudctrl values for CPU frequency 32 Mhz +static PROGMEM_DECLARE(uint16_t, baudctrl_32mhz[]) = { + 0x031D, // Baud: 1200 + 0x01ED, // Baud: 2400 + 0xFDDC, // Baud: 4800 + 0xF5CC, // Baud: 9600 + 0xE5BC, // Baud: 19200 + 0xC5AC, // Baud: 38400 + 0x6EA8, // Baud: 57600 +}; +//! @} + +//! Input parameters when initializing RS232 and similar modes. +typedef struct usart_rs232_options { + //! Set baud rate of the USART (unused in slave modes). + uint32_t baudrate; + + //! Number of bits to transmit as a character (5 to 9). + USART_CHSIZE_t charlength; + + //! Parity type: USART_PMODE_DISABLED_gc, USART_PMODE_EVEN_gc, + //! USART_PMODE_ODD_gc. + USART_PMODE_t paritytype; + + //! Number of stop bits between two characters: + //! true: 2 stop bits + //! false: 1 stop bit + bool stopbits; + +} usart_rs232_options_t; + +//! Input parameters when initializing SPI master mode. +typedef struct usart_spi_options { + //! Set baud rate of the USART in SPI mode. + uint32_t baudrate; + + //! SPI transmission mode. + uint8_t spimode; + + uint8_t data_order; +} usart_spi_options_t; + +//! USART interrupt levels +enum usart_int_level_t { + USART_INT_LVL_OFF = 0x00, + USART_INT_LVL_LO = 0x01, + USART_INT_LVL_MED = 0x02, + USART_INT_LVL_HI = 0x03, +}; + +/** + * \brief Enable USART receiver. + * + * \param usart Pointer to the USART module + */ +static inline void usart_rx_enable(USART_t *usart) +{ + (usart)->CTRLB |= USART_RXEN_bm; +} + +/** + * \brief Disable USART receiver. + * + * \param usart Pointer to the USART module. + */ +static inline void usart_rx_disable(USART_t *usart) +{ + (usart)->CTRLB &= ~USART_RXEN_bm; +} + +/** + * \brief Configure the USART frame format. + * + * Sets the frame format, Frame Size, parity mode and number of stop bits. + * + * \param usart Pointer to the USART module + * \param charSize The character size. Use USART_CHSIZE_t type. + * \param parityMode The parity Mode. Use USART_PMODE_t type. + * \param twoStopBits Enable two stop bit mode. Use bool type. + */ +static inline void usart_format_set(USART_t *usart, USART_CHSIZE_t charSize, + USART_PMODE_t parityMode, bool twoStopBits) +{ + (usart)->CTRLC = (uint8_t)charSize | parityMode + | (twoStopBits ? USART_SBMODE_bm : 0); +} + +/** + * \brief Enable USART transmitter. + * + * \param usart Pointer to the USART module. + */ +static inline void usart_tx_enable(USART_t *usart) +{ + (usart)->CTRLB |= USART_TXEN_bm; +} + +/** + * \brief Disable USART transmitter. + * + * \param usart Pointer to the USART module. + */ +static inline void usart_tx_disable(USART_t *usart) +{ + (usart)->CTRLB &= ~USART_TXEN_bm; +} + +/** + * \brief Set USART RXD interrupt level. + * + * Sets the interrupt level on RX Complete interrupt. + * + * \param usart Pointer to the USART module. + * \param level Interrupt level of the RXD interrupt. + */ +static inline void usart_set_rx_interrupt_level(USART_t *usart, + enum usart_int_level_t level) +{ + (usart)->CTRLA = ((usart)->CTRLA & ~USART_RXCINTLVL_gm) | + (level << USART_RXCINTLVL_gp); +} + +/** + * \brief Set USART TXD interrupt level. + * + * Sets the interrupt level on TX Complete interrupt. + * + * \param usart Pointer to the USART module. + * \param level Interrupt level of the TXD interrupt. + */ +static inline void usart_set_tx_interrupt_level(USART_t *usart, + enum usart_int_level_t level) +{ + (usart)->CTRLA = ((usart)->CTRLA & ~USART_TXCINTLVL_gm) | + (level << USART_TXCINTLVL_gp); +} + +/** + * \brief Set USART DRE interrupt level. + * + * Sets the interrupt level on Data Register interrupt. + * + * \param usart Pointer to the USART module. + * \param level Interrupt level of the DRE interrupt. + * Use USART_DREINTLVL_t type. + */ +static inline void usart_set_dre_interrupt_level(USART_t *usart, + enum usart_int_level_t level) +{ + (usart)->CTRLA = ((usart)->CTRLA & ~USART_DREINTLVL_gm) | + (level << USART_DREINTLVL_gp); +} + +/** + * \brief Set the mode the USART run in. + * + * Set the mode the USART run in. The default mode is asynchronous mode. + * + * \param usart Pointer to the USART module register section. + * \param usartmode Selects the USART mode. Use USART_CMODE_t type. + * + * USART modes: + * - 0x0 : Asynchronous mode. + * - 0x1 : Synchronous mode. + * - 0x2 : IrDA mode. + * - 0x3 : Master SPI mode. + */ +static inline void usart_set_mode(USART_t *usart, USART_CMODE_t usartmode) +{ + (usart)->CTRLC = ((usart)->CTRLC & (~USART_CMODE_gm)) | usartmode; +} + +/** + * \brief Check if data register empty flag is set. + * + * \param usart The USART module. + */ +static inline bool usart_data_register_is_empty(USART_t * usart) +{ + return (usart)->STATUS & USART_DREIF_bm; +} + +/** + * \brief Checks if the RX complete interrupt flag is set. + * + * Checks if the RX complete interrupt flag is set. + * + * \param usart The USART module. + */ +static inline bool usart_rx_is_complete(USART_t * usart) +{ + return (usart)->STATUS & USART_RXCIF_bm; +} + +/** + * \brief Checks if the TX complete interrupt flag is set. + * + * Checks if the TX complete interrupt flag is set. + * + * \param usart The USART module. + */ +static inline bool usart_tx_is_complete(USART_t * usart) +{ + return (usart)->STATUS & USART_TXCIF_bm; +} + +/** + * \brief Clear TX complete interrupt flag. + * + * \param usart The USART module. + */ +static inline void usart_clear_tx_complete(USART_t * usart) +{ + (usart)->STATUS = USART_TXCIF_bm; +} + +/** + * \brief Clear RX complete interrupt flag. + * + * \param usart The USART module. + */ +static inline void usart_clear_rx_complete(USART_t *usart) +{ + (usart)->STATUS = USART_RXCIF_bm; +} + +/** + * \brief Write a data to the USART data register. + * + * \param usart The USART module. + * \param txdata The data to be transmitted. + */ +static inline void usart_put(USART_t * usart, uint8_t txdata) +{ + (usart)->DATA = txdata; +} + +/** + * \brief Read a data to the USART data register. + * + * \param usart The USART module. + * + * \return The received data + */ +static inline uint8_t usart_get(USART_t * usart) +{ + return (usart)->DATA; +} + +/** + * \brief Performs a data transfer on the USART in SPI mode. + * + * \param usart The USART module. + * \param txdata The data to be transmitted. + * + * \return The received data + */ +static inline uint8_t usart_spi_transmit(USART_t * usart, + uint8_t txdata) +{ + while (usart_data_register_is_empty(usart) == false); + usart_put(usart, txdata); + while (!usart_tx_is_complete(usart)); + usart_clear_tx_complete(usart); + return usart_get(usart); +} + +bool usart_init_rs232(USART_t *usart, const usart_rs232_options_t *opt); +void usart_init_spi(USART_t * usart, const usart_spi_options_t * opt); + +status_code_t usart_putchar(USART_t * usart, uint8_t c); +uint8_t usart_getchar(USART_t * usart); + +void usart_set_bsel_bscale_value(USART_t *usart, uint16_t bsel, uint8_t bscale); +void usart_set_baudrate_precalculated(USART_t *usart, uint32_t baud, + uint32_t cpu_hz); +bool usart_set_baudrate(USART_t *usart, uint32_t baud, uint32_t cpu_hz); +void usart_spi_set_baudrate(USART_t * usart, uint32_t baud, uint32_t cpu_hz); +//! @} + +#ifdef __cplusplus +} +#endif + +/** + * \page xmega_usart_quickstart Quick start guide for USART module + * + * This is the quick start guide for the \ref usart_group "USART module", with + * step-by-step instructions on how to configure and use the driver in a + * selection of use cases. + * + * The use cases contain several code fragments. The code fragments in the + * steps for setup can be copied into a custom initialization function, while + * the steps for usage can be copied into, e.g., the main application function. + * + * \section usart_basic_use_case Basic use case + * \section usart_use_cases USART use cases + * - \ref usart_basic_use_case + * - \subpage usart_use_case_1 + * + * \section usart_basic_use_case Basic use case - transmit a character + * In this use case, the USART module is configured for: + * - Using USARTD0 + * - Baudrate: 9600 + * - Character length: 8 bit + * - Parity mode: Disabled + * - Stop bit: None + * - RS232 mode + * + * \section usart_basic_use_case_setup Setup steps + * + * \subsection usart_basic_use_case_setup_prereq Prerequisites + * -# \ref sysclk_group + * \subsection usart_basic_use_case_setup_code Example code + * The following configuration must be added to the project (typically to a + * conf_usart.h file, but it can also be added to your main application file.) + * \code + * #define USART_SERIAL &USARTD0 + * #define USART_SERIAL_BAUDRATE 9600 + * #define USART_SERIAL_CHAR_LENGTH USART_CHSIZE_8BIT_gc + * #define USART_SERIAL_PARITY USART_PMODE_DISABLED_gc + * #define USART_SERIAL_STOP_BIT false + * \endcode + * + * Add to application initialization: + * \code + * sysclk_init(); + * static usart_rs232_options_t USART_SERIAL_OPTIONS = { + * .baudrate = USART_SERIAL_BAUDRATE, + * .charlength = USART_SERIAL_CHAR_LENGTH, + * .paritytype = USART_SERIAL_PARITY, + * .stopbits = USART_SERIAL_STOP_BIT + * }; + * sysclk_enable_module(SYSCLK_PORT_D, PR_USART0_bm); + * usart_init_rs232(USART_SERIAL, &USART_SERIAL_OPTIONS); + * \endcode + * + * \subsection usart_basic_use_case_setup_flow Workflow + * -# Initialize system clock: + * - \code sysclk_init(); \endcode + * - \note Not always required, but since the \ref usart_group driver is + * dependent on \ref sysclk_group it is good practise to initialize + * this module. + * -# Create USART options struct: + * - \code + * static usart_rs232_options_t USART_SERIAL_OPTIONS = { + * .baudrate = USART_SERIAL_BAUDRATE, + * .charlength = USART_SERIAL_CHAR_LENGTH, + * .paritytype = USART_SERIAL_PARITY, + * .stopbits = USART_SERIAL_STOP_BIT + * }; + * \endcode + * -# Enable the clock for the USART module: + * - \code sysclk_enable_module(SYSCLK_PORT_D, PR_USART0_bm); \endcode + * -# Initialize in RS232 mode: + * - \code usart_init_rs232(USART_SERIAL, &USART_SERIAL_OPTIONS); + * \endcode + * + * \section usart_basic_use_case_usage Usage steps + * + * \subsection usart_basic_use_case_usage_code Example code + * Add to application C-file: + * \code + * usart_putchar(USART_SERIAL, 'a'); + * \endcode + * + * \subsection usart_basic_use_case_usage_flow Workflow + * -# Send an 'a' character via USART + * - \code usart_putchar(USART_SERIAL, 'a'); \endcode + */ + +/** + * \page usart_use_case_1 USART receive character and echo back + * + * In this use case, the USART module is configured for: + * - Using USARTD0 + * - Baudrate: 9600 + * - Character length: 8 bit + * - Parity mode: Disabled + * - Stop bit: None + * - RS232 mode + * + * The use case waits for a received character on the configured USART and + * echoes the character back to the same USART. + * + * \section usart_use_case_1_setup Setup steps + * + * \subsection usart_use_case_1_setup_prereq Prerequisites + * -# \ref sysclk_group + * + * \subsection usart_use_case_1_setup_code Example code + * -# The following configuration must be added to the project (typically to a + * conf_usart.h file, but it can also be added to your main application file.): + * \code + * #define USART_SERIAL &USARTD0 + * #define USART_SERIAL_BAUDRATE 9600 + * #define USART_SERIAL_CHAR_LENGTH USART_CHSIZE_8BIT_gc + * #define USART_SERIAL_PARITY USART_PMODE_DISABLED_gc + * #define USART_SERIAL_STOP_BIT false + * \endcode + * + * A variable for the received byte must be added: + * \code uint8_t received_byte; \endcode + * + * Add to application initialization: + * \code + * sysclk_init(); + * static usart_rs232_options_t USART_SERIAL_OPTIONS = { + * .baudrate = USART_SERIAL_BAUDRATE, + * .charlength = USART_SERIAL_CHAR_LENGTH, + * .paritytype = USART_SERIAL_PARITY, + * .stopbits = USART_SERIAL_STOP_BIT + * }; + * sysclk_enable_module(SYSCLK_PORT_D, PR_USART0_bm); + * usart_init_rs232(USART_SERIAL, &USART_SERIAL_OPTIONS); + * \endcode + * + * \subsection usart_use_case_1_setup_flow Workflow + * -# Initialize system clock: + * - \code sysclk_init(); \endcode + * - \note Not always required, but since the \ref usart_group driver is + * dependent on \ref sysclk_group it is good practise to initialize + * this module. + * -# Create USART options struct: + * - \code + * static usart_rs232_options_t USART_SERIAL_OPTIONS = { + * .baudrate = USART_SERIAL_BAUDRATE, + * .charlength = USART_SERIAL_CHAR_LENGTH, + * .paritytype = USART_SERIAL_PARITY, + * .stopbits = USART_SERIAL_STOP_BIT + * }; + * \endcode + * -# Enable the clock for the USART module: + * - \code sysclk_enable_module(SYSCLK_PORT_D, PR_USART0_bm); \endcode + * -# Initialize in RS232 mode: + * - \code usart_init_rs232(USART_SERIAL, &USART_SERIAL_OPTIONS); + * \endcode + * + * \section usart_use_case_1_usage Usage steps + * + * \subsection usart_use_case_1_usage_code Example code + * Add to, e.g., main loop in application C-file: + * \code + * received_byte = usart_getchar(USART_SERIAL); + * usart_putchar(USART_SERIAL, received_byte); + * \endcode + * + * \subsection usart_use_case_1_usage_flow Workflow + * -# Wait for reception of a character: + * - \code received_byte = usart_getchar(USART_SERIAL); \endcode + * -# Echo the character back: + * - \code usart_putchar(USART_SERIAL, received_byte); \endcode + */ + +#endif // _USART_H_ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/wdt/wdt.c b/bacnet-stack/ports/xplained/ASF/xmega/drivers/wdt/wdt.c new file mode 100644 index 00000000..a7c048e5 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/wdt/wdt.c @@ -0,0 +1,216 @@ +/** + * \file + * + * \brief AVR XMEGA WatchDog Timer driver. + * + * Copyright (c) 2011 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include "compiler.h" +#include "ccp.h" +#include "wdt.h" + +/*! \brief Set Watchdog timeout period. + * + * This function sets the coded field of the WDT timeout period. + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the CTRL register. Interrupts are + * automatically ignored during the change enable period. The function will + * wait for the WDT to be synchronized to the WDT clock domain before + * proceeding + * + * \param to_period WDT timeout coded period + */ +void wdt_set_timeout_period(enum wdt_timeout_period_t to_period) +{ + uint8_t temp = (WDT_PER_gm & (to_period << WDT_PER_gp)) | + (WDT.CTRL & WDT_ENABLE_bm) | (1 << WDT_CEN_bp); + ccp_write_io((void *)&WDT.CTRL, temp); + wdt_wait_while_busy(); +} + + +/*! \brief Set Watchdog window period. + * + * This function sets the coded field of the WDT closed window period. + * Note that this setting is available only if the WDT is enabled (hardware + * behaviour relayed by software). + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the WINCTRL register. Interrupts + * are automatically ignored during the change enable period. The function will + * wait for the WDT to be synchronized to the WDT clock domain before + * proceeding + * + * \param win_period Window coded period + * + * \retval true The WDT was enabled and the setting is done. + * false The WDT is disabled and the setting is discarded. + */ +bool wdt_set_window_period(enum wdt_window_period_t win_period) +{ + if (!(wdt_is_enabled())) { + return false; + } + uint8_t temp = (WDT_WPER_gm & (win_period << WDT_WPER_gp)) | + (WDT.WINCTRL & WDT_WEN_bm) | (1 << WDT_WCEN_bp); + ccp_write_io((void *)&WDT.WINCTRL, temp); + wdt_wait_while_busy(); + return true; +} + + +/*! \brief Disable Watchdog. + * + * This function disables the WDT without changing period settings. + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the CTRL register. Interrupts are + * automatically ignored during the change enable period. Disable functions + * operate asynchronously with immediate effect. + */ +void wdt_disable(void) +{ + uint8_t temp = (WDT.CTRL & ~WDT_ENABLE_bm) | (1 << WDT_CEN_bp); + ccp_write_io((void *)&WDT.CTRL, temp); +} + + +/*! \brief Enable Watchdog. + * + * This function enables the WDT without changing period settings. + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the CTRL register. Interrupts are + * automatically ignored during the change enable period. The function will + * wait for the WDT to be synchronized to the WDT clock domain before + * proceeding + */ +void wdt_enable(void) +{ + uint8_t temp = (WDT.CTRL & WDT_PER_gm) | + (1 << WDT_ENABLE_bp) | (1 << WDT_CEN_bp); + ccp_write_io((void *)&WDT.CTRL, temp); + wdt_wait_while_busy(); +} + + +/*! \brief Disable Watchdog window mode without changing period settings. + * + * This function disables the WDT window mode without changing period settings. + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the WINCTRL register. Interrupts + * are automatically ignored during the change enable period. Disable functions + * operate asynchronously with immediate effect. + * + * \retval true The WDT was enabled and the window mode is disabled. + * false The WDT (& the window mode) is already disabled. + */ +bool wdt_disable_window_mode(void) +{ + if (!(wdt_is_enabled())) { + return false; + } + uint8_t temp = (WDT.WINCTRL & ~WDT_WEN_bm) | (1 << WDT_WCEN_bp); + ccp_write_io((void *)&WDT.WINCTRL, temp); + + return true; +} + + +/*! \brief Enable Watchdog window mode. + * + * This function enables the WDT window mode without changing period settings. + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the WINCTRL register. Interrupts + * are automatically ignored during the change enable period. The function will + * wait for the WDT to be synchronized to the WDT clock domain before + * proceeding + * + * \retval true The WDT was enabled and the setting is done. + * false The WDT is disabled and the setting is discarded. + */ +bool wdt_enable_window_mode(void) +{ + if (!(wdt_is_enabled())) { + return false; + } + uint8_t temp = (WDT.WINCTRL & WDT_WPER_gm) | + (1 << WDT_WEN_bp) | (1 << WDT_WCEN_bp); + ccp_write_io((void *)&WDT.WINCTRL, temp); + wdt_wait_while_busy(); + return true; +} + + +/*! \brief Reset MCU via Watchdog. + * + * This function generates an hardware microcontroller reset using the WDT. + * + * The function loads enables the WDT in window mode. Executing a "wdr" asm + * instruction when the windows is closed, provides a quick mcu reset. + * + */ +void wdt_reset_mcu(void) +{ +uint8_t temp; + /* + * WDT enabled (minimum timeout period for max. security) + */ + temp = WDT_PER_8CLK_gc | (1 << WDT_ENABLE_bp) | (1 << WDT_CEN_bp); + ccp_write_io((void *)&WDT.CTRL, temp); + wdt_wait_while_busy(); + /* + * WDT enabled (maximum window period for max. security) + */ + temp = WDT_WPER_8KCLK_gc | (1 << WDT_WEN_bp) | (1 << WDT_WCEN_bp); + ccp_write_io((void *)&WDT.WINCTRL, temp); + wdt_wait_while_busy(); + /* + * WDT Reset during window => WDT generates an Hard Reset. + */ + wdt_reset(); + /* + * No exit to prevent the execution of the following instructions. + */ + while (true) { + /* Wait for Watchdog reset. */ + } +} diff --git a/bacnet-stack/ports/xplained/ASF/xmega/drivers/wdt/wdt.h b/bacnet-stack/ports/xplained/ASF/xmega/drivers/wdt/wdt.h new file mode 100644 index 00000000..523d75c6 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/drivers/wdt/wdt.h @@ -0,0 +1,420 @@ +/** + * \file + * + * \brief AVR XMEGA WatchDog Timer driver. + * + * Copyright (c) 2011-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _WDT_H_ +#define _WDT_H_ + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +extern "C" { +#endif +/**INDENT-ON**/ +/// @endcond + +#include "compiler.h" + +/** + * \defgroup wdt_group Watchdog Timer (WDT) + * + * See \ref wdt_quickstart. + * + * This is a driver for configuring, enabling, disabling and use of the on-chip + * WDT. + * + * \section dependencies Dependencies + * + * The WDT module depends on the following modules: + * - \ref ccp_group for writing in a CCP-protected 8-bit I/O register. + * + * @{ + */ + + +//! Watchdog timeout period setting +enum wdt_timeout_period_t { + //! Timeout period = 8 cycles or 8 ms @ 3.3V + WDT_TIMEOUT_PERIOD_8CLK = (0x00), + //! Timeout period = 16 cycles or 16 ms @ 3.3V + WDT_TIMEOUT_PERIOD_16CLK = (0x01), + //! Timeout period = 32 cycles or 32m s @ 3.3V + WDT_TIMEOUT_PERIOD_32CLK = (0x02), + //! Timeout period = 64 cycles or 64ms @ 3.3V + WDT_TIMEOUT_PERIOD_64CLK = (0x03), + //! Timeout period = 125 cycles or 125ms @ 3.3V + WDT_TIMEOUT_PERIOD_125CLK = (0x04), + //! 250 cycles or 250ms @ 3.3V) + WDT_TIMEOUT_PERIOD_250CLK = (0x05), + //! Timeout period = 500 cycles or 500ms @ 3.3V + WDT_TIMEOUT_PERIOD_500CLK = (0x06), + //! Timeout period =1K cycles or 1s @ 3.3V + WDT_TIMEOUT_PERIOD_1KCLK = (0x07), + //! Timeout period = 2K cycles or 2s @ 3.3V + WDT_TIMEOUT_PERIOD_2KCLK = (0x08), + //! Timeout period = 4K cycles or 4s @ 3.3V + WDT_TIMEOUT_PERIOD_4KCLK = (0x09), + //! Timeout period = 8K cycles or 8s @ 3.3V + WDT_TIMEOUT_PERIOD_8KCLK = (0x0A), +}; + +//! Watchdog window period setting +enum wdt_window_period_t { + //! Window period = 8 cycles or 8 ms @ 3.3V + WDT_WINDOW_PERIOD_8CLK = (0x00), + //! Window period = 16 cycles or 16 ms @ 3.3V + WDT_WINDOW_PERIOD_16CLK = (0x01), + //! Window period = 32 cycles or 32m s @ 3.3V + WDT_WINDOW_PERIOD_32CLK = (0x02), + //! Window period = 64 cycles or 64ms @ 3.3V + WDT_WINDOW_PERIOD_64CLK = (0x03), + //! Window period = 125 cycles or 125ms @ 3.3V + WDT_WINDOW_PERIOD_125CLK = (0x04), + //! 250 cycles or 250ms @ 3.3V) + WDT_WINDOW_PERIOD_250CLK = (0x05), + //! Window period = 500 cycles or 500ms @ 3.3V + WDT_WINDOW_PERIOD_500CLK = (0x06), + //! Window period =1K cycles or 1s @ 3.3V + WDT_WINDOW_PERIOD_1KCLK = (0x07), + //! Window period = 2K cycles or 2s @ 3.3V + WDT_WINDOW_PERIOD_2KCLK = (0x08), + //! Window period = 4K cycles or 4s @ 3.3V + WDT_WINDOW_PERIOD_4KCLK = (0x09), + //! Window period = 8K cycles or 8s @ 3.3V + WDT_WINDOW_PERIOD_8KCLK = (0x0A), +}; + + +/*! \brief This macro resets (clears/refreshes) the Watchdog Timer. + */ +#if defined(__GNUC__) +#define wdt_reset() __asm__ __volatile__("wdr"); +#elif defined(__ICCAVR__) +#define wdt_reset() __watchdog_reset(); +#else +#error Unsupported compiler. +#endif + + +/*! \brief Wait until WD settings are synchronized to the WD clock domain. + * + */ +static inline void wdt_wait_while_busy(void) +{ + while ((WDT.STATUS & WDT_SYNCBUSY_bm) == WDT_SYNCBUSY_bm) { + // Wait until synchronization + } +} + + +/*! \brief Check if the Watchdog Enable flag is set. + * + * \retval false WDT disabled + * true WDT enabled + */ +static inline bool wdt_is_enabled(void) +{ + return ((WDT.CTRL & WDT_ENABLE_bm) == WDT_ENABLE_bm); +} + + +/*! \brief Check if the Watchdog Window mode flag is set. + * + * \retval false WDT Window disabled + * true WDT Window enabled + */ +static inline bool wdt_window_mode_is_enabled(void) +{ + return ((WDT.WINCTRL & WDT_WEN_bm) == WDT_WEN_bm); +} + + +/*! \brief Gets the Watchdog timeout period. + * + * This function reads the value of the WDT timeout period. + * + * \retval The WDT timeout period. + */ +static inline enum wdt_timeout_period_t wdt_get_timeout_period(void) +{ + return ((enum wdt_timeout_period_t) + ((WDT.CTRL & WDT_PER_gm) >> WDT_PER_gp)); +} + + +/*! \brief Gets the Watchdog window period. + * + * This function reads the value of the WDT closed window coded period. + * + * \retval The WDT window period. + */ +static inline enum wdt_window_period_t wdt_get_window_period(void) +{ + return ((enum wdt_window_period_t) + ((WDT.WINCTRL & WDT_WPER_gm) >> WDT_WPER_gp)); +} + + +/*! \brief Set Watchdog timeout period. + * + * This function sets the coded field of the WDT timeout period. + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the CTRL register. Interrupts are + * automatically ignored during the change enable period. The function will + * wait for the WDT to be synchronized to the WDT clock domain before + * proceeding + * + * \param to_period WDT timeout coded period + */ +void wdt_set_timeout_period(enum wdt_timeout_period_t to_period); + + +/*! \brief Set Watchdog window period. + * + * This function sets the coded field of the WDT closed window period. + * Note that this setting is available only if the WDT is enabled (hardware + * behaviour relayed by software). + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the WINCTRL register. Interrupts + * are automatically ignored during the change enable period. The function will + * wait for the WDT to be synchronized to the WDT clock domain before + * proceeding + * + * \param win_period Window coded period + * + * \retval true The WDT was enabled and the setting is done. + * false The WDT is disabled and the setting is discarded. + */ +bool wdt_set_window_period(enum wdt_window_period_t win_period); + + +/*! \brief Disable Watchdog. + * + * This function disables the WDT without changing period settings. + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the CTRL register. Interrupts are + * automatically ignored during the change enable period. Disable functions + * operate asynchronously with immediate effect. + */ +void wdt_disable(void); + + +/*! \brief Enable Watchdog. + * + * This function enables the WDT without changing period settings. + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the CTRL register. Interrupts are + * automatically ignored during the change enable period. The function will + * wait for the WDT to be synchronized to the WDT clock domain before + * proceeding + */ +void wdt_enable(void); + + +/*! \brief Disable Watchdog window mode without changing period settings. + * + * This function disables the WDT window mode without changing period settings. + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the WINCTRL register. Interrupts + * are automatically ignored during the change enable period. Disable functions + * operate asynchronously with immediate effect. + * + * \retval true The WDT was enabled and the window mode is disabled. + * false The WDT (& the window mode) is already disabled. + */ +bool wdt_disable_window_mode(void); + + +/*! \brief Enable Watchdog window mode. + * + * This function enables the WDT window mode without changing period settings. + * + * The function writes the correct signature to the Configuration + * Change Protection register before writing the WINCTRL register. Interrupts + * are automatically ignored during the change enable period. The function will + * wait for the WDT to be synchronized to the WDT clock domain before + * proceeding + * + * \retval true The WDT was enabled and the setting is done. + * false The WDT is disabled and the setting is discarded. + */ +bool wdt_enable_window_mode(void); + + +/*! \brief Reset MCU via Watchdog. + * + * This function generates an hardware microcontroller reset using the WDT. + * + * The function loads enables the WDT in window mode. Executing a "wdr" asm + * instruction when the windows is closed, provides a quick mcu reset. + * + */ +void wdt_reset_mcu(void); + + +//! @} + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +} +#endif +/**INDENT-ON**/ +/// @endcond + +/** + * \page wdt_quickstart Quick start guide for WDT driver + * + * This is the quick start guide for the \ref wdt_group, with + * step-by-step instructions on how to configure and use the driver in a + * selection of use cases. + * + * The use cases contain several code fragments. The code fragments in the + * steps for setup can be copied into a custom initialization function, while + * the steps for usage can be copied into, e.g., the main application function. + * + * \section wdt_basic_use_case Basic use case + * \section wdt_use_cases WDT use cases + * - \ref wdt_basic_use_case + * - \subpage wdt_use_case_1 + * + * \section wdt_basic_use_case Basic use case - Reset WDT in standard mode + * In this use case, the WDT is configured for: + * - Standard mode + * - Timeout period of 8 ms + * + * The use case enables the WDT, and resets it after 5 ms to prevent system + * reset after time out period of 8 ms. + * + * \section wdt_basic_use_case_setup Setup steps + * + * \subsection wdt_basic_use_case_setup_prereq Prerequisites + * For the setup code of this use case to work, the following must + * be added to the project: + * -# \ref group_common_services_delay "Busy-Wait Delay Routines" + * + * \subsection wdt_basic_use_case_setup_code Example code + * Add to application initialization: + * \code + * wdt_set_timeout_period(WDT_TIMEOUT_PERIOD_8CLK); + * wdt_enable(); + * \endcode + * + * \subsection wdt_basic_use_case_setup_flow Workflow + * -# Set timeout period to 8 cycles or 8 ms: + * - \code wdt_set_timeout_period(WDT_TIMEOUT_PERIOD_8CLK); \endcode + * -# Enable WDT: + * - \code wdt_enable(); \endcode + * \section wdt_basic_use_case_usage Usage steps + * + * \subsection wdt_basic_use_case_usage_code Example code + * Add to, e.g., main loop in application C-file: + * \code + * delay_ms(5); + * wdt_reset(); + * \endcode + * + * \subsection wdt_basic_use_case_usage_flow Workflow + * -# Wait for 5 ms: + * - \code delay_ms(5); \endcode + * -# Reset the WDT before the timeout period is over to prevent system reset: + * - \code wdt_reset(); \endcode + */ + +/** + * \page wdt_use_case_1 Reset WDT in window mode + * + * In this use case, the WDT is configured for: + * - Window mode + * - Timeout period of 16 ms + * + * The use case enables the WDT in window mode, and resets it after 10 ms to + * prevent system reset before window timeout after 8 ms and after time out + * period of 16 ms. + * + * \section wdt_use_case_1_setup Setup steps + * + * \subsection usart_use_case_1_setup_prereq Prerequisites + * For the setup code of this use case to work, the following must + * be added to the project: + * -# \ref group_common_services_delay "Busy-Wait Delay Routines" + * + * \subsection wdt_use_case_1_setup_code Example code + * Add to application initialization: + * \code + * wdt_set_timeout_period(WDT_TIMEOUT_PERIOD_16CLK); + * wdt_enable(); + * wdt_set_window_period(WDT_TIMEOUT_PERIOD_8CLK); + * wdt_enable_window_mode(); + * \endcode + * + * \subsection wdt_use_case_1_setup_flow Workflow + * -# Set timeout period to 16 cycles or 16 ms: + * - \code wdt_set_timeout_period(WDT_TIMEOUT_PERIOD_16CLK); \endcode + * -# Enable WDT: + * - \code wdt_enable(); \endcode + * -# Set window period to 8 cycles or 8 ms: + * - \code wdt_set_window_period(WDT_TIMEOUT_PERIOD_8CLK); \endcode + * -# Enable window mode: + * - \code wdt_enable_window_mode(); \endcode + * + * \section wdt_use_case_1_usage Usage steps + * + * \subsection wdt_use_case_1_usage_code Example code + * Add to, e.g., main loop in application C-file: + * \code + * delay_ms(10); + * wdt_reset(); + * \endcode + * + * \subsection wdt_use_case_1_usage_flow Workflow + * -# Wait for 10 ms to not reset the WDT before window timeout: + * - \code delay_ms(10); \endcode + * -# Reset the WDT before the timeout period is over to prevent system reset: + * - \code wdt_reset(); \endcode + */ + +#endif // _WDT_H_ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/services/pwm/pwm.c b/bacnet-stack/ports/xplained/ASF/xmega/services/pwm/pwm.c new file mode 100644 index 00000000..bc9e95d8 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/services/pwm/pwm.c @@ -0,0 +1,244 @@ +/** + * \file + * + * \brief PWM service for XMEGA. + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + + #include + +/** + * \brief Calculate TC settings from PWM frequency + * + * This function will find the correct TC settings (clock prescaler and + * period) which will give the wanted PWM frequency. + * + * \note Since we want to be able to run the PWM at all duty-cycles ranging + * from 0-100%, we require a period of at least 100 to achieve this. Thus, the + * highest possible PWM frequency is CPU frequency / 100. + * + * \param config Pointer to PWM configuration. + * \param freq_hz Wanted PWM frequency in Hz. + */ +void pwm_set_frequency(struct pwm_config *config, uint16_t freq_hz) +{ + uint32_t cpu_hz = sysclk_get_cpu_hz(); + uint16_t smallest_div; + uint16_t dividor; + + /* Avoid division by zero. */ + Assert(freq_hz != 0); + + /* Calculate the smallest divider for the requested frequency + related to the CPU frequency */ + smallest_div = cpu_hz / freq_hz / 0xFFFF; + if (smallest_div < 1) { + dividor = 1; + config->clk_sel = PWM_CLK_DIV1; + } else if (smallest_div < 2) { + dividor = 2; + config->clk_sel = PWM_CLK_DIV2; + } else if (smallest_div < 4) { + dividor = 4; + config->clk_sel = PWM_CLK_DIV4; + } else if (smallest_div < 8) { + dividor = 8; + config->clk_sel = PWM_CLK_DIV8; + } else if (smallest_div < 64) { + dividor = 64; + config->clk_sel = PWM_CLK_DIV64; + } else if (smallest_div < 256) { + dividor = 256; + config->clk_sel = PWM_CLK_DIV256; + } else { + dividor = 1024; + config->clk_sel = PWM_CLK_DIV1024; + } + + /* Calculate the period from the just found divider */ + config->period = cpu_hz / dividor / freq_hz; + + /* Make sure our period is at least 100 ticks so we are able to provide + a full range (0-100% duty cycle */ + if (config->period < 100) { + /* The period is too short. */ + config->clk_sel = PWM_CLK_OFF; + config->period = 0; + Assert(false); + } +} + +/** + * \brief Initialize PWM configuration struct and set correct I/O pin to output + * + * \param config Pointer to PWM configuration struct. + * \param tc \ref pwm_tc_t "TC" to use for this PWM. + * \param channel \ref pwm_channel_t "CC channel" to use for this PWM. + * \param freq_hz Frequency to use for this PWM. + */ +void pwm_init(struct pwm_config *config, enum pwm_tc_t tc, + enum pwm_channel_t channel, uint16_t freq_hz) +{ + /* Number of channels for this TC */ + uint8_t num_chan = 0; + UNUSED(num_chan); + + /* Set TC and correct I/O pin to output */ + switch (tc) { +#if defined(TCC0) + case PWM_TCC0: + config->tc = &TCC0; + PORTC.DIR |= (1 << (channel-1)); + num_chan = 4; + break; +#endif +#if defined(TCC1) + case PWM_TCC1: + config->tc = &TCC1; + PORTC.DIR |= (1 << (channel+3)); + num_chan = 2; + break; +#endif +#if defined(TCD0) + case PWM_TCD0: + config->tc = &TCD0; + PORTD.DIR |= (1 << (channel-1)); + num_chan = 4; + break; +#endif +#if defined(TCD1) + case PWM_TCD1: + config->tc = &TCD1; + PORTD.DIR |= (1 << (channel+3)); + num_chan = 2; + break; +#endif + +#if defined(TCE0) + case PWM_TCE0: + config->tc = &TCE0; + PORTE.DIR |= (1 << (channel-1)); + num_chan = 4; + break; +#endif +#if defined(TCE1) + case PWM_TCE1: + config->tc = &TCE1; + PORTE.DIR |= (1 << (channel+3)); + num_chan = 2; + break; +#endif + +#if defined(TCF0) + case PWM_TCF0: + config->tc = &TCF0; + PORTF.DIR |= (1 << (channel-1)); + num_chan = 4; + break; +#endif +#if defined(TCF1) + case PWM_TCF1: + config->tc = &TCF1; + PORTF.DIR |= (1 << (channel+3)); + num_chan = 2; + break; +#endif + default: + Assert(false); + break; + } + + /* Make sure we are not given a channel number larger + than this TC can handle */ + Assert(channel <= num_chan); + config->channel = channel; + + /* Set the correct cc_mask */ + switch (channel) { + case PWM_CH_A: + config->cc_mask = TC_CCAEN; + break; + case PWM_CH_B: + config->cc_mask = TC_CCBEN; + break; + case PWM_CH_C: + config->cc_mask = TC_CCCEN; + break; + case PWM_CH_D: + config->cc_mask = TC_CCDEN; + break; + default: + Assert(false); + break; + } + + /* Enable peripheral clock for this TC */ + tc_enable(config->tc); + + /* Set this TC's waveform generator in single slope mode */ + tc_set_wgm(config->tc, TC_WG_SS); + + /* Default values (disable TC and set minimum period)*/ + config->period = 0; + config->clk_sel = PWM_CLK_OFF; + tc_write_clock_source(config->tc, PWM_CLK_OFF); + + /* Set the PWM frequency */ + pwm_set_frequency(config, freq_hz); +} + +/** + * \brief Start a PWM channel + * + * This function enables a channel with a given duty cycle. + * + * \param *config Pointer to the PWM configuration struct + * \param duty_cycle_scale Duty cycle as a value between 0 and 100. + */ +void pwm_start(struct pwm_config *config, uint8_t duty_cycle_scale) +{ + /* Set given duty cycle */ + pwm_set_duty_cycle_percent(config, duty_cycle_scale); + /* Set correct TC period */ + tc_write_period(config->tc, config->period); + /* Enable CC channel for this TC */ + tc_enable_cc_channels(config->tc, config->cc_mask); + /* Enable TC by setting correct clock prescaler */ + tc_write_clock_source(config->tc, config->clk_sel); +} diff --git a/bacnet-stack/ports/xplained/ASF/xmega/services/pwm/pwm.h b/bacnet-stack/ports/xplained/ASF/xmega/services/pwm/pwm.h new file mode 100644 index 00000000..5d1ab76c --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/services/pwm/pwm.h @@ -0,0 +1,352 @@ +/** + * \file + * + * \brief PWM service for XMEGA. + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#ifndef PWM_H +#define PWM_H + +#include "tc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup pwm_group XMEGA Pulse Width Modulation (PWM) service + * + * See \ref pwm_quickstart. + * + * This is a service for single slope wave form generation on the XMEGA. + * It provides functions for enabling, disabling and configuring the TC modules + * in single slope PWM mode. + * + * The API uses a \ref pwm_config "structure" which contain the configuration. + * This structure must be set up before the PWM can be started. + * + * \section dependencies Dependencies + * This driver depends on the following modules: + * - \ref tc_group to set up TC in PWM mode. + * @{ + */ + + /** + * \brief PWM compare channel index + */ +enum pwm_channel_t { + /** Channel A. PWM output on pin 0 */ + PWM_CH_A = 1, + /** Channel B. PWM output on pin 1 */ + PWM_CH_B = 2, + /** Channel C. PWM output on pin 2 */ + PWM_CH_C = 3, + /** Channel D. PWM output on pin 3 */ + PWM_CH_D = 4, +}; + + /** + * \brief Valid timer/counters to use + * \note Not all timer/counters are available on all devices. + * Please refer to the datasheet for more information on what + * timer/counters are available for the device you are using. + */ +enum pwm_tc_t { + /** PWM on port C, pin 0, 1, 2 or 3 (depending on + \ref pwm_channel_t "channel") */ + PWM_TCC0, + /** PWM on port C, pin 4 or 5 (depending on + \ref pwm_channel_t "channel") */ + PWM_TCC1, + /** PWM on port D, pin 0, 1, 2 or 3 (depending on + \ref pwm_channel_t "channel") */ + PWM_TCD0, + /** PWM on port D, pin 4 or 5 (depending on + \ref pwm_channel_t "channel") */ + PWM_TCD1, + /** PWM on port E, pin 0, 1, 2 or 3 (depending on + \ref pwm_channel_t "channel") */ + PWM_TCE0, + /** PWM on port E, pin 4 or 5 (depending on + \ref pwm_channel_t "channel") */ + PWM_TCE1, + /** PWM on port F, pin 0, 1, 2 or 3 (depending on + \ref pwm_channel_t "channel") */ + PWM_TCF0, + /** PWM on port F, pin 4 or 5 (depending on + \ref pwm_channel_t "channel") */ + PWM_TCF1, +}; + + /** + * \brief Valid clock source indexes + */ +enum pwm_clk_sel { + PWM_CLK_OFF = TC_CLKSEL_OFF_gc, + PWM_CLK_DIV1 = TC_CLKSEL_DIV1_gc, + PWM_CLK_DIV2 = TC_CLKSEL_DIV2_gc, + PWM_CLK_DIV4 = TC_CLKSEL_DIV4_gc, + PWM_CLK_DIV8 = TC_CLKSEL_DIV8_gc, + PWM_CLK_DIV64 = TC_CLKSEL_DIV64_gc, + PWM_CLK_DIV256 = TC_CLKSEL_DIV256_gc, + PWM_CLK_DIV1024 = TC_CLKSEL_DIV1024_gc, +}; + + /** + * \brief PWM configuration + */ +struct pwm_config { + void *tc; + enum pwm_channel_t channel; + enum tc_cc_channel_mask_enable_t cc_mask; + enum pwm_clk_sel clk_sel; + uint16_t period; +}; + +/** \brief Interrupt callback type */ +typedef void (*pwm_callback_t) (void); + +void pwm_init(struct pwm_config *config, enum pwm_tc_t tc, + enum pwm_channel_t channel, uint16_t freq_hz); +void pwm_set_frequency(struct pwm_config *config, uint16_t freq_hz); +void pwm_start(struct pwm_config *config, uint8_t duty_cycle_scale); + +/** + * \brief Function to set PWM duty cycle + * + * The duty cycle can be set on a scale between 0-100%. This value + * will be used to update the CCx register for the selected PWM channel. + * + * \param *config Pointer to the PWM configuration struct + * \param duty_cycle_scale Duty cycle as a value between 0 and 100. + */ +static inline void pwm_set_duty_cycle_percent(struct pwm_config *config, + uint8_t duty_cycle_scale) +{ + Assert( duty_cycle_scale <= 100 ); + tc_write_cc_buffer(config->tc, config->channel, + (uint16_t)(((uint32_t)config->period * + (uint32_t)duty_cycle_scale) / 100)); +} + +/** + * \brief Function that stops the PWM timer + * + * The PWM timer is stopped by writing the prescaler register to "clock off" + * + * \param *config Pointer to the PWM configuration struct + */ +static inline void pwm_stop(struct pwm_config *config) +{ + tc_write_clock_source(config->tc, TC_CLKSEL_OFF_gc); +} + +/** + * \brief Disable the PWM timer + * + * This function disables the peripheral clock for the timer and shut down + * module when unused in order to save power. + * + * \param *config Pointer to the PWM configuration struct + */ +static inline void pwm_disable(struct pwm_config *config) +{ + pwm_stop(config); + tc_disable(config->tc); +} + +/** + * \brief Function that resets the PWM timer + * + * This function reset the CNT register for the selected timer used for PWM + * + * \param *config Pointer to the PWM configuration struct + */ +static inline void pwm_timer_reset(struct pwm_config *config) +{ + tc_write_count(config->tc, 0); +} + +/** + * \brief Callback function for timer overflow interrupts + * + * This function enables T/C overflow interrupts (low level interrupts) + * and defines the callback function for the overflow ISR interrupt routine. + * + * \param *config Pointer to the PWM configuration struct + * \param callback Callback function + */ +static inline void pwm_overflow_int_callback(struct pwm_config *config, + pwm_callback_t callback) +{ + tc_set_overflow_interrupt_level(config->tc, TC_INT_LVL_LO); + tc_set_overflow_interrupt_callback(config->tc, callback); +} + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** + * \page pwm_quickstart Quickstart guide for AVR XMEGA PWM service + * + * This is the quickstart guide for the \ref pwm_group, + * with step-by-step instructions on how to configure and use the service in a + * selection of use cases. + * + * The use cases contain several code fragments. The code fragments in the + * steps for setup can be copied into a custom initialization function, while + * the steps for usage can be copied into, e.g., the main application function. + * + * \section basic_use_case Basic use case + * In the most basic use case, we configure one PWM channel in non-interrupt + * mode. + * + * \section pwm_basic_use_case_setup Setup steps + * \subsection pwm_basic_use_case_setup_code Example code + * Add to application C-file: + * \code + * struct pwm_config pwm_cfg; + * + * sysclk_init(); + * pwm_init(&pwm_cfg, PWM_TCE0, PWM_CH_A, 500); + * \endcode + * + * \subsection pwm_basic_use_case_setup_flow Workflow + * -# Ensure that \ref conf_clock.h is present for the driver. + * \note This file is only for the driver and should not be included by the + * user. + * -# Define config struct for PWM module: + * \code struct pwm_config pwm_cfg; \endcode + * -# Initialize sysclock module: + * \code sysclk_init();\endcode + * -# Initialize config struct and set up PWM with frequency of 500 Hz.\n + * \code pwm_init(&pwm_cfg, PWM_TCE0, PWM_CH_A, 500); \endcode + * \note Since the timer/counter \ref PWM_TCE0 and channel \ref PWM_CH_A + * is used, the PWM will be output on port E, pin 0. + * See \ref pwm_tc_t and \ref pwm_channel_t for more information + * on what port/pin is used for different timer/counters. + + * \attention This step must not be skipped or the initial content of the + * structs will be unpredictable, possibly causing misconfiguration. + * + * \section pwm_basic_use_case_usage Usage steps + * \subsection pwm_basic_use_case_usage_code Example code + * Add to, e.g., main loop in application C-file: + * \code pwm_start(&pwm_config, 50); \endcode + * + * \subsection pwm_basic_use_case_usage_flow Workflow + * -# Start PWM with 50% duty cycle: + * \code pwm_start(&pwm_config, 50); \endcode + * + * \section pwm_use_cases Advanced use cases + * For more advanced use of the PWM service, see the following use cases: + * - \subpage pwm_use_case_1 : PWM with interrupt + */ +/** + * \page pwm_use_case_1 Use case #1 + * In this use case the PWM module is configured with overflow interrupt. + * + * \section pwm_use_case_1_setup Setup steps + * \subsection pwm_use_case_1_setup_code Example code + * + * Add to application C-file: + * \code + * struct pwm_config pwm_cfg; + * + * void my_callback(void) + * { + * do_something(); + * } + + * void pwm_init(void) + * { + * pmic_init(); + * sysclk_init(); + * + * cpu_irq_enable(); + * + * pwm_init(&pwm_cfg, PWM_TCE0, PWM_CH_A, 75); + * pwm_overflow_int_callback(&pwm_cfg, my_callback); + * } + * \endcode + * + * \subsection pwm_use_case_1_setup_flow Workflow + * -# Define config struct for PWM module: + * \code struct pwm_config pwm_cfg; \endcode + * -# Define a callback function in the application which does whatever task + * you want it to do: + * \code + * void my_callback(void) + * { + * do_something(); + * } + * \endcode + * -# Initialize interrupt controller module: + * \code pmic_init();\endcode + * -# Initialize sysclock module: + * \code sysclk_init();\endcode + * -# Enable global interrupts: + * \code cpu_irq_enable();\endcode + * -# Initialize config struct and set up PWM with frequency of 75 Hz: + * \code pwm_init(&pwm_cfg, PWM_TCE0, PWM_CH_A, 75); \endcode + * \note Since the timer/counter \ref PWM_TCE0 and channel \ref PWM_CH_A + * is used, the PWM will be output on port E, pin 0. + * See \ref pwm_tc_t and \ref pwm_channel_t for more information + * on what port/pin is used for different timer/counters. + * \attention This step must not be skipped or the initial content of the + * structs will be unpredictable, possibly causing misconfiguration. + * -# Set callback function on PWM TC channel overflow: + * \code pwm_overflow_int_callback(&pwm_cfg, my_callback); \endcode + * + * \section pwm_use_case_1_usage Usage steps + * \subsection pwm_use_case_1_usage_code Example code + * Add to, e.g., main loop in application C-file: + * \code pwm_start(&pwm_cfg, 50); \endcode + * + * \subsection pwm_basic_use_case_usage_flow Workflow + * -# Start PWM with 50% duty cycle: + * \code pwm_start(&pwm_cfg, 50); \endcode + * + */ + +#endif /* PWM_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/services/timeout/timeout.c b/bacnet-stack/ports/xplained/ASF/xmega/services/timeout/timeout.c new file mode 100644 index 00000000..5eb8fd34 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/services/timeout/timeout.c @@ -0,0 +1,231 @@ +/** + * \file timeout.c + * + * \brief Timeout service for XMEGA + * + * Copyright (C) 2011-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#include +#include + +/* Check if RTC32 is defined, otherwise use RTC as default */ +#if defined(CLOCK_SOURCE_RTC32) + #include +#else + #include +#endif + +/** \brief Timeout timekeeping data */ +struct timeout_struct { + /** + * Current count-down value. Counts down for every tick. + * Will be considered as expired when it reaches 0, and + * may then be reloaded with period. + */ + uint16_t count; + + /** + * Period between expires. Used to reload count. + * If 0, the count won't be reloaded. + */ + uint16_t period; +}; + +/** Array of configurable timeout timekeeping data */ +static struct timeout_struct timeout_array[TIMEOUT_COUNT]; + +/** Bitmask of active timeouts */ +static uint8_t timeout_active; + +/** Bitmask of expired timeouts */ +static uint8_t timeout_expired; + +/** + * \brief Callback function for RTC compare interrupt handler + * + * The function executes when the RTC compare interrupt occurs and loop + * through all timeout channels. The timeout_array[channel_index] which + * contains the remaining ticks before timeout is decremented and the timeout + * active/expired masks are updated. + */ +static void tick_handler(uint32_t time) +{ + uint8_t i; + + /* Loop through all timeout channels */ + for (i = 0; i < TIMEOUT_COUNT; i++) { + /* Skip processing on current channel if not active */ + if (!(timeout_active & (1 << i))) { + continue; + } + + /* Decrement current channel with one tick */ + timeout_array[i].count--; + + /* Skip further processing on current channel if not expired */ + if (timeout_array[i].count) { + continue; + } else { + /* Update expired bit mask with current channel */ + timeout_expired |= 1 << i; + + /* If Periodic timer, reset timeout counter to period + * time */ + if (timeout_array[i].period) { + timeout_array[i].count + = timeout_array[i].period; + } + /* If not periodic timeout, set current channel to + * in-active */ + else { + timeout_active &= ~(1 << i); + } + } + } + /* Reset RTC before next tick */ + rtc_set_time(0); + rtc_set_alarm(TIMEOUT_COMP); +} + +/** + * \brief Initialize timeout + * + * Initializes timeout counter for desired tick rate and starts it. The device + * interrupt controller should be initialized prior to calling this function, + * and global interrupts must be enabled. + * + * \note If the service is configured to use the asynchronous RTC32 module, + * there are restrictions on the timeout period that can be used - see + * to \ref rtc32_min_alarm_time for details. + */ +void timeout_init(void) +{ + rtc_init(); + rtc_set_callback(tick_handler); + rtc_set_time(0); + rtc_set_alarm(TIMEOUT_COMP); +} + +/** + * \brief Start periodic timeout with a specific start timeout + * + * \param id \ref timeout_id_t + * \param period Time period in number of ticks + * \param offset Time to first timeout in number of ticks + */ +void timeout_start_offset(timeout_id_t id, uint16_t period, uint16_t offset) +{ + /* Check that ID within the TIMEOUT_COUNT range */ + if (id < TIMEOUT_COUNT) { + /* Disable interrupts before tweaking the bitmasks */ + irqflags_t flags; + flags = cpu_irq_save(); + + /* Update timeout struct with offset and period */ + timeout_array[id].count = offset; + timeout_array[id].period = period; + + /* Set current timeout channel bitmasks to active and not + * expired */ + timeout_active |= 1 << id; + timeout_expired &= ~(1 << id); + + /* Restore interrupts */ + cpu_irq_restore(flags); + } +} + +/** + * \brief Start singleshot timeout + * + * \param id \ref timeout_id_t + * \param timeout Timeout in number of ticks + */ +void timeout_start_singleshot(timeout_id_t id, uint16_t timeout) +{ + timeout_start_offset(id, 0, timeout); +} + +/** + * \brief Start periodic timeout + * + * \param id \ref timeout_id_t + * \param period Time period in number of ticks + */ +void timeout_start_periodic(timeout_id_t id, uint16_t period) +{ + timeout_start_offset(id, period, period); +} + +/** + * \brief Test and clear expired flag for running timeout + * + * \param id \ref timeout_id_t + * \retval true Timer have expired; clearing expired flag + * \retval false Timer still running + */ +bool timeout_test_and_clear_expired(timeout_id_t id) +{ + /* Check that ID within the TIMEOUT_COUNT range */ + if (id < TIMEOUT_COUNT) { + irqflags_t flags; + + /* Check if timeout has expired */ + if (timeout_expired & (1 << id)) { + flags = cpu_irq_save(); + timeout_expired &= ~(1 << id); + cpu_irq_restore(flags); + return true; + } + } + + return false; +} + +/** + * \brief Stop running timeout + * + * \param id \ref timeout_id_t + */ +void timeout_stop(timeout_id_t id) +{ + irqflags_t flags; + flags = cpu_irq_save(); + timeout_active &= ~(1 << id); + cpu_irq_restore(flags); +} diff --git a/bacnet-stack/ports/xplained/ASF/xmega/services/timeout/timeout.h b/bacnet-stack/ports/xplained/ASF/xmega/services/timeout/timeout.h new file mode 100644 index 00000000..48ae448a --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/services/timeout/timeout.h @@ -0,0 +1,380 @@ +/** + * \file timeout.h + * + * \brief Timeout service for XMEGA + * + * Copyright (C) 2011-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef TIMEOUT_H +#define TIMEOUT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "conf_timeout.h" + +/** + * \defgroup timeout_group Timeout service XMEGA + * + * See \ref timeout_quickstart. + * + * The timeout service uses the asynchronous RTC/RTC32 in order to have a + * system tick. Typical tick rate is 1-1000Hz. Clock sources available: + * - Internal 32kHz ULP oscillator + * - Internal 32kHz calibrated RC oscillator + * - External 32kHz crystal oscillator + * - External clock (Not available on all devices) + * + * The timeout service is configurable to a number of independent timeout + * channels, each with different delay setup in a number of ticks. Both + * singleshot and periodic timeouts are supported. + * + * As this service provides a software layer on top of the RTC/RTC32 module it + * will have some performance penalty, so for high performance it would be + * recommended to implement a more specific use by implementing your own + * interrupt handler based on this as a reference. + * + * \section timeout_configuration Configuration + * Configuration is done in the config file : conf_timeout.h + * + * Configuration defines: + * - \ref TIMEOUT_CLOCK_SOURCE_HZ : Frequency of clock source, used in + * calculation of tick rate + * + * - \ref TIMEOUT_COUNT : Number of independent timeout channels + * (Max 8 channels) + * + * - \ref TIMEOUT_TICK_HZ : Desired tick rate in Hz + * + * - \ref CLOCK_SOURCE_RTC32 Used to disable the RTC module (default) + * and use the RTC32 module found in ATxmegaA3B + * and ATxmegaA3BU. + * + * \section tc_timeout_interface Interface + * The timeout internal setup needs to be configured and this is done by the + * function tc_timeout_init(). + * + * There are different functions for starting a timer: + * - \ref timeout_start_singleshot() : Start a singleshot timeout. + * - \ref timeout_start_periodic() : Start a periodic timeout. + * - \ref timeout_start_offset() : Start a periodic timeout with a specific + * start offset. + * + * Polling for timer status can be done with + * \ref timeout_test_and_clear_expired(), and this will also clear the + * expired flag in case of periodic timer. + * + * A running timer can be stopped with \ref timeout_stop(). + * + * Common to all the function arguments are a timeout identifier, this is a + * number starting from 0 to identify the timeout channel. Maximum of this + * parameter is controlled by the configuration \ref TIMEOUT_COUNT. + * + * The start timeout functions uses timeout values represented in number of + * ticks. + * + * \subsection tc_timeout_usage Usage + * First of all, the include file is needed: + * \code #include "timeout.h" \endcode + * + * Then the timeout internals need to be set up by calling: + * \code timeout_init(); \endcode + * + * For simple usage starting a singleshot timeout for timeout id 0 and a timeout + * value of 100 ticks: + * \code + * tc_timeout_start_singleshot(0, 100); + * while (!timeout_test_and_clear_expired(0)); + * // do whats needed after timeout has expired + * \endcode + * + * \section tc_timeout_accuracy Accuracy + * Since this is a timeout layer on top of a system tick; the trigger time of a + * timeout is fully depending on this system tick. This means that you might + * not know when the next tick will count down your timeout, and this inaccuracy + * can be from 0 to 1 system tick. + * + * E.g.: If you want a timeout of 1 system tick and use 1 as your timeout + * value, this might trigger immediately. So, if you have a requirement to wait + * at least 1 system tick, it would be recommended to use the requested value + * + 1. + * + * However, if you know the system tick has passed or are using periodic timeout + * you can be confident in the timing. + */ + + +// Test for missing configurations +#if !defined(TIMEOUT_CLOCK_SOURCE_HZ) +# error "configuration define missing: TIMEOUT_CLOCK_SOURCE_HZ" +#endif + +#if !defined(TIMEOUT_TICK_HZ) +# error "configuration define missing: TIMEOUT_TICK_HZ" +#endif + +#if !defined(TIMEOUT_COUNT) +# error "configuration define missing: TIMEOUT_COUNT" +#endif + +// Check if timeout count is within allowed range +#if (TIMEOUT_COUNT > 8) +# error "TIMEOUT_COUNT outside allowed range" +#endif + + +// Calculate tick rate +#define TIMEOUT_COMP TIMEOUT_CLOCK_SOURCE_HZ / TIMEOUT_TICK_HZ + +/** + * \brief Timeout identifier + * + * Index for timeout channel to use. Limited by max value configured with \ref + * TIMEOUT_COUNT. + */ +typedef uint8_t timeout_id_t; + +// API functions +void timeout_init(void); +void timeout_start_singleshot(timeout_id_t id, uint16_t timeout); +void timeout_start_periodic(timeout_id_t id, uint16_t period); +void timeout_start_offset(timeout_id_t id, uint16_t period, + uint16_t start_offset); +bool timeout_test_and_clear_expired(timeout_id_t id); +void timeout_stop(timeout_id_t id); + +#ifdef __cplusplus +} +#endif + + /** + * \page timeout_quickstart Quick start guide for Timeout service + * + * This is the quick start guide for the \ref timeout_group, with + * step-by-step instructions on how to configure and use the driver in a + * selection of use cases. + * + * The use cases contain several code fragments. The code fragments in the + * steps for setup can be copied into a custom initialization function, while + * the steps for usage can be copied into, e.g., the main application function. + * + * \section timeout_use_cases Timeout use cases + * - \ref timeout_basic_use_case + * - \subpage timeout_use_case_1 + * + * \section timeout_basic_use_case Basic use case - Toggle LEDs with periodic timeout + * In this use case, two periodic timeouts are used to toggle two leds. + * + * \section timeout_basic_use_case_setup Setup steps + * + * \subsection timeout_basic_use_case_setup_prereq Prerequisites + * For the setup code of this use case to work, the following must + * be added to the project: + * -# \ref sysclk_group + * -# \ref pmic_group + * -# \ref gpio_group + * -# \ref rtc_group + * -# Configuration info for the timeout service must be added to the + * conf_timeout.h file (located in the config folder): + * \code + * #define TIMEOUT_CLOCK_SOURCE_HZ 1024 + * #define TIMEOUT_COUNT 8 + * #define TIMEOUT_TICK_HZ 4 + * \endcode + * -# Configuration info for the RTC driver must be added to the + * conf_rtc.h file (located in the config folder): + * \code + * #define CONFIG_RTC_PRESCALER RTC_PRESCALER_DIV1_gc + * #define CONFIG_RTC_CLOCK_SOURCE CLK_RTCSRC_ULP_gc + * \endcode + * + * \subsection timeout_basic_use_case_setup_code Example code + * The following must be added to the project: + * \code + * #define TIMEOUT_0 0 + * #define TIMEOUT_1 1 + * \endcode + * + * Add to application initialization: + * \code + * sysclk_init(); + * pmic_init(); + * timeout_init(); + * timeout_start_periodic(TIMEOUT_0, 1); + * timeout_start_periodic(TIMEOUT_1, 2); + * \endcode + * + * \subsection timeout_basic_use_case_setup_flow Workflow + * -# Initialize system clock: + * - \code sysclk_init(); \endcode + * -# Initialize the PMIC driver: + * - \code pmic_init(); \endcode + * -# Initialize timeout service: + * - \code timout_init(); \endcode + * -# Start timeout channel 0 with a period of 1 tick: + * - \code timeout_start_periodic(TIMEOUT_0, 1); \endcode + * -# Start timeout channel 1 with a period of 2 ticks: + * - \code timeout_start_periodic(TIMEOUT_1, 2); \endcode + * + * \section timeout_basic_use_case_usage Usage steps + * + * \subsection timeout_basic_use_case_usage_code Example code + * Add to application C-file: + * \code + * while (1) { + * if (timeout_test_and_clear_expired(TIMEOUT_0)) { + * gpio_toggle_pin(LED0_GPIO); + * } + * if (timeout_test_and_clear_expired(TIMEOUT_1)) { + * gpio_toggle_pin(LED1_GPIO); + * } + * } + * \endcode + * + * \subsection timeout_basic_use_case_usage_flow Workflow + * -# Check if timeout on channel 0 has expired, and toggle led if it has: + * - \code + * if (timeout_test_and_clear_expired(TIMEOUT_0)) { + * gpio_toggle_pin(LED0_GPIO); + * } + * \endcode + * -# Check if timeout on channel 1 has expired, and toggle led if it has: + * - \code + * if (timeout_test_and_clear_expired(TIMEOUT_1)) { + * gpio_toggle_pin(LED1_GPIO); + * } + * \endcode + */ + +/** + * \page timeout_use_case_1 Debounce filter on a button + * + * In this use case, a simple debounce filter on a button will be set up. + * + * \section timeout_use_case_1_setup Setup steps + * + * \subsection timeout_use_case_1_setup_prereq Prerequisites + * For the setup code of this use case to work, the following must + * be added to the project: + * -# \ref sysclk_group + * -# \ref pmic_group + * -# \ref gpio_group + * -# \ref rtc_group + * -# Configuration info for the timeout service must be added to the + * conf_timeout.h file (located in the config folder): + * \code + * #define TIMEOUT_CLOCK_SOURCE_HZ 1024 + * #define TIMEOUT_COUNT 1 + * #define TIMEOUT_TICK_HZ 100 + * \endcode + * -# Configuration info for the RTC driver must be added to the + * conf_rtc.h file (located in the config folder): + * \code + * #define CONFIG_RTC_PRESCALER RTC_PRESCALER_DIV1_gc + * #define CONFIG_RTC_CLOCK_SOURCE CLK_RTCSRC_ULP_gc + * \endcode + * + * \subsection timeout_use_case_1_setup_code Example code + * The following must be added to the project: + * \code + * #define DEBOUNCE_TIMEOUT 0 + * #define DEBOUNCE_TICKS (50 * TIMEOUT_TICK_HZ / 1000) + * \endcode + * + * Add to application initialization: + * \code + * sysclk_init(); + * pmic_init(); + * timeout_init(); + * \endcode + * + * \subsection timeout_use_case_1_setup_flow Workflow + * -# Initialize system clock: + * - \code sysclk_init(); \endcode + * -# Initialize the PMIC driver: + * - \code pmic_init(); \endcode + * -# Initialize timeout service: + * - \code timout_init(); \endcode + * + * \subsection timeout_use_case_1_usage_code Example code + * Add to application C-file: + * \code + * bool button_pressed; + * bool button_previous_state_pressed = false; + * while (1) { + * button_pressed = gpio_pin_is_low(GPIO_PUSH_BUTTON_0); + * if (button_previous_state_pressed != button_pressed) { + * timeout_start_singleshot(DEBOUNCE_TIMEOUT, DEBOUNCE_TICKS); + * button_previous_state_pressed = button_pressed; + * } + * + * if (timeout_test_and_clear_expired(DEBOUNCE_TIMEOUT)) { + * if (button_pressed) { + * gpio_toggle_pin(LED0_GPIO); + * } + * } + * } + * \endcode + * + * \subsection timeout_use_case_1_usage_flow Workflow + * -# Create a variable to hold state of push button: + * - \code bool button_pressed; \endcode + * -# Create a variable to hold previous state of push button: + * - \code bool button_previous_state_pressed; \endcode + * -# Get button state: + * - \code button_pressed = gpio_pin_is_low(GPIO_PUSH_BUTTON_0); \endcode + * -# Check if button state has changed since last iteration: + * - \code if (button_previous_state_pressed != button_pressed) \endcode + * -# Start debounce timeout: + * - \code + * timeout_start_singleshot(DEBOUNCE_TIMEOUT, DEBOUNCE_TICKS); + * \endcode + * -# Set previous state of button: + * - \code button_previous_state_pressed = button_pressed; \endcode + * -# Check if debounce timeout has expired: + * - \code if (timeout_test_and_clear_expired(DEBOUNCE_TIMEOUT)) \endcode + * -# Check if button is pressed down: + * - \code if (button_pressed) \endcode + * -# Toggle led: + * - \code gpio_toggle_pin(LED0_GPIO); \endcode + */ + +#endif /* TIMEOUT_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/utils/assembler.h b/bacnet-stack/ports/xplained/ASF/xmega/utils/assembler.h new file mode 100644 index 00000000..c4451b3f --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/utils/assembler.h @@ -0,0 +1,156 @@ +/** + * \file + * + * \brief Assembler abstraction layer and utilities + * + * Copyright (c) 2009 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef ASSEMBLER_H_INCLUDED +#define ASSEMBLER_H_INCLUDED + +#if !defined(__ASSEMBLER__) && !defined(__IAR_SYSTEMS_ASM__) \ + && !defined(__DOXYGEN__) +# error This file may only be included from assembly files +#endif + +#if defined(__ASSEMBLER__) +# include "assembler/gas.h" +# include +#elif defined(__IAR_SYSTEMS_ASM__) +# include "assembler/iar.h" +# include +#endif + +/** + * \ingroup group_xmega_utils + * \defgroup assembler_group Assembler Support + * + * This group provides a good handful of macros intended to smooth out + * the differences between various assemblers, similar to what compiler.h does + * for compilers, except that assemblers tend to be much less standardized than + * compilers. + * + * @{ + */ + +//! \name Control Statements +//@{ +/** + * \def REPEAT(count) + * \brief Repeat the following statements \a count times + */ +/** + * \def END_REPEAT() + * \brief Mark the end of the statements to be repeated + */ +/** + * \def SET_LOC(offset) + * \brief Set the location counter to \a offset + */ +/** + * \def END_FILE() + * \brief Mark the end of the file + */ +//@} + +//! \name Data Objects +//@{ +/** + * \def FILL_BYTES(count) + * \brief Allocate space for \a count bytes + */ +//@} + +//! \name Symbol Definition +//@{ +/** + * \def L(name) + * \brief Turn \a name into a local symbol, if possible + */ +/** + * \def EXTERN_SYMBOL(name) + * \brief Declare \a name as an external symbol referenced by this file + */ +/** + * \def FUNCTION(name) + * \brief Define a file-local function called \a name + */ +/** + * \def PUBLIC_FUNCTION(name) + * \brief Define a globally visible function called \a name + */ +/** + * \def WEAK_FUNCTION(name) + * \brief Define a weak function called \a name + * + * Weak functions are only referenced if no strong definitions are found + */ +/** + * \def WEAK_FUNCTION_ALIAS(name, strong_name) + * \brief Define \a name as a weak alias for the function \a strong_name + * \sa WEAK_FUNCTION + */ +/** + * \def END_FUNC(name) + * \brief Mark the end of the function called \a name + */ +//@} + +//! \name Section Definition +//@{ +/** + * \def TEXT_SECTION(name) + * \brief Start a new section containing executable code + */ +/** + * \def RODATA_SECTION(name) + * \brief Start a new section containing read-only data + */ +/** + * \def DATA_SECTION(name) + * \brief Start a new section containing writeable initialized data + */ +/** + * \def BSS_SECTION(name) + * \brief Start a new section containing writeable zero-initialized data + */ +//@} + +//! @} + +#endif /* ASSEMBLER_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/utils/assembler/gas.h b/bacnet-stack/ports/xplained/ASF/xmega/utils/assembler/gas.h new file mode 100644 index 00000000..b2760b88 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/utils/assembler/gas.h @@ -0,0 +1,121 @@ +/** + * \file + * + * \brief Assembler abstraction layer: GNU Assembler specifics + * + * Copyright (c) 2009 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef ASSEMBLER_GAS_H_INCLUDED +#define ASSEMBLER_GAS_H_INCLUDED + +#ifndef __DOXYGEN__ + + /* IAR doesn't accept dots in macro names */ + .macro ld_addr, reg, sym + lda.w \reg, \sym + .endm + + /* Define a function \a name that is either globally visible or only + * file-local. + */ + .macro gas_begin_func name, is_public + .if \is_public + .global \name + .endif + .section .text.\name, "ax", @progbits + .type \name, @function + \name : + .endm + + /* Define a function \a name that is either globally visible or only + * file-local in a given segment. + */ + .macro gas_begin_func_segm name, is_public, segment + .if \is_public + .global \name + .endif + .section .\segment, "ax", @progbits + .type \name, @function + \name : + .endm + + /* Define \a name as a weak alias for the function \a strong_name */ + .macro gas_weak_function_alias name, strong_name + .global \name + .weak \name + .type \name, @function + .set \name, \strong_name + .endm + + /* Define a weak function called \a name */ + .macro gas_weak_function name + .weak \name + gas_begin_func \name 1 + .endm + +#define REPEAT(count) .rept count +#define END_REPEAT() .endr +#define FILL_BYTES(count) .fill count +#define SET_LOC(offset) .org offset +#define L(name) .L##name +#define EXTERN_SYMBOL(name) + +#define TEXT_SECTION(name) \ + .section name, "ax", @progbits +#define RODATA_SECTION(name) \ + .section name, "a", @progbits +#define DATA_SECTION(name) \ + .section name, "aw", @progbits +#define BSS_SECTION(name) \ + .section name, "aw", @nobits + +#define FUNCTION(name) gas_begin_func name 0 +#define PUBLIC_FUNCTION(name) gas_begin_func name 1 +#define PUBLIC_FUNCTION_SEGMENT(name, segment) \ + gas_begin_func_segm name 1 segment +#define WEAK_FUNCTION(name) gas_weak_function name +#define WEAK_FUNCTION_ALIAS(name, strong_name) \ + gas_weak_function_alias name strong_name +#define END_FUNC(name) \ + .size name, . - name + +#define END_FILE() + +#endif /* __DOXYGEN__ */ + +#endif /* ASSEMBLER_GAS_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/utils/bit_handling/clz_ctz.h b/bacnet-stack/ports/xplained/ASF/xmega/utils/bit_handling/clz_ctz.h new file mode 100644 index 00000000..f3f15513 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/utils/bit_handling/clz_ctz.h @@ -0,0 +1,235 @@ +/** + * \file + * + * \brief CLZ/CTZ C implementation. + * + * Copyright (c) 2009 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef CLZ_CTH_H +#define CLZ_CTH_H + +/** + * \brief Count leading zeros in unsigned integer + * + * This macro takes unsigned integers of any size, and evaluates to a call to + * the clz-function for its size. These functions count the number of zeros, + * starting with the MSB, before a one occurs in the integer. + * + * \param x Unsigned integer to count the leading zeros in. + * + * \return The number of leading zeros in \a x. + */ +#define clz(x) compiler_demux_size(sizeof(x), clz, (x)) + +/** + * \internal + * \brief Count leading zeros in unsigned, 8-bit integer + * + * \param x Unsigned byte to count the leading zeros in. + * + * \return The number of leading zeros in \a x. + */ +__always_inline static uint8_t +clz8 (uint8_t x) +{ + uint8_t bit = 0; + + if (x & 0xf0) + { + x >>= 4; + } + else + { + bit += 4; + } + + if (x & 0x0c) + { + x >>= 2; + } + else + { + bit += 2; + } + + if (!(x & 0x02)) + { + bit++; + } + + return bit; + +} + +/** + * \internal + * \brief Count leading zeros in unsigned, 16-bit integer + * + * \param x Unsigned word to count the leading zeros in. + * + * \return The number of leading zeros in \a x. + */ +__always_inline static uint8_t +clz16 (uint16_t x) +{ + uint8_t bit = 0; + + if (x & 0xff00) + { + x >>= 8; + } + else + { + bit += 8; + } + + return bit + clz8 (x); +} + +/** + * \internal + * \brief Count leading zeros in unsigned, 32-bit integer + * + * \param x Unsigned double word to count the leading zeros in. + * + * \return The number of leading zeros in \a x. + */ +__always_inline static uint8_t +clz32 (uint32_t x) +{ + uint8_t bit = 0; + + if (x & 0xffff0000) + { + x >>= 16; + } + else + { + bit += 16; + } + + return bit + clz16 (x); +} + +/** + * \brief Count trailing zeros in unsigned integer + * + * This macro takes unsigned integers of any size, and evaluates to a call to + * the ctz-function for its size. These functions count the number of zeros, + * starting with the LSB, before a one occurs in the integer. + * + * \param x Unsigned integer to count the trailing zeros in. + * + * \return The number of trailing zeros in \a x. + */ +#define ctz(x) compiler_demux_size(sizeof(x), ctz, (x)) + +/** + * \internal + * \brief Count trailing zeros in unsigned, 8-bit integer + * + * \param x Unsigned byte to count the trailing zeros in. + * + * \return The number of leading zeros in \a x. + */ +__always_inline static uint8_t +ctz8 (uint8_t x) +{ + uint8_t bit = 0; + + if (!(x & 0x0f)) + { + bit += 4; + x >>= 4; + } + if (!(x & 0x03)) + { + bit += 2; + x >>= 2; + } + if (!(x & 0x01)) + bit++; + + return bit; +} + +/** + * \internal + * \brief Count trailing zeros in unsigned, 16-bit integer + * + * \param x Unsigned word to count the trailing zeros in. + * + * \return The number of trailing zeros in \a x. + */ +__always_inline static uint8_t +ctz16 (uint16_t x) +{ + uint8_t bit = 0; + + if (!(x & 0x00ff)) + { + bit += 8; + x >>= 8; + } + + return bit + ctz8 (x); +} + +/** + * \internal + * \brief Count trailing zeros in unsigned, 32-bit integer + * + * \param x Unsigned double word to count the trailing zeros in. + * + * \return The number of trailing zeros in \a x. + */ +__always_inline static uint8_t +ctz32 (uint32_t x) +{ + uint8_t bit = 0; + + if (!(x & 0x0000ffff)) + { + bit += 16; + x >>= 16; + } + + return bit + ctz16 (x); +} + +#endif /* CLZ_CTZ_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/utils/compiler.h b/bacnet-stack/ports/xplained/ASF/xmega/utils/compiler.h new file mode 100644 index 00000000..eda8f841 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/utils/compiler.h @@ -0,0 +1,1162 @@ +/** + * \file + * + * \brief Commonly used includes, types and macros. + * + * Copyright (c) 2010-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef UTILS_COMPILER_H +#define UTILS_COMPILER_H + +/** + * \defgroup group_xmega_utils XMEGA compiler driver + * + * Compiler abstraction layer and code utilities for 8-bit AVR. + * This module provides various abstraction layers and utilities to make code compatible between different compilers. + * + * \{ + */ + +#if defined(__GNUC__) +# include +# include +#elif defined(__ICCAVR__) +# include +# include +#else +# error Unsupported compiler. +#endif + +#include +#include +#include +#include + +#include + +#ifdef __ICCAVR__ +/*! \name Compiler Keywords + * + * Port of some keywords from GCC to IAR Embedded Workbench. + */ +//! @{ +#define __asm__ asm +#define __inline__ inline +#define __volatile__ +//! @} +#endif + +/** + * \def UNUSED + * \brief Marking \a v as a unused parameter or value. + */ +#define UNUSED(v) (void)(v) + +/** + * \def unused + * \brief Marking \a v as a unused parameter or value. + */ +#define unused(v) do { (void)(v); } while(0) + +/** + * \def barrier + * \brief Memory barrier + */ +#ifdef __GNUC__ +# define barrier() asm volatile("" ::: "memory") +#else +# define barrier() asm ("") +#endif + +/** + * \brief Emit the compiler pragma \a arg. + * + * \param arg The pragma directive as it would appear after \e \#pragma + * (i.e. not stringified). + */ +#define COMPILER_PRAGMA(arg) _Pragma(#arg) + +/* + * AVR arch does not care about alignment anyway. + */ +#define COMPILER_PACK_RESET(alignment) +#define COMPILER_PACK_SET(alignment) + +/** + * \brief Set aligned boundary. + */ +#if (defined __GNUC__) +#define COMPILER_ALIGNED(a) __attribute__((__aligned__(a))) +#elif (defined __ICCAVR__) +#define COMPILER_ALIGNED(a) COMPILER_PRAGMA(data_alignment = a) +#endif + +/** + * \brief Set word-aligned boundary. + */ +#if (defined __GNUC__) +#define COMPILER_WORD_ALIGNED __attribute__((__aligned__(2))) +#elif (defined __ICCAVR__) +#define COMPILER_WORD_ALIGNED COMPILER_PRAGMA(data_alignment = 2) +#endif + +/** + * \name Tag functions as deprecated + * + * Tagging a function as deprecated will produce a warning when and only + * when the function is called. + * + * Usage is to add the __DEPRECATED__ symbol before the function definition. + * E.g.: + * __DEPRECATED__ uint8_t some_deprecated_function (void) + * { + * ... + * } + * + * \note Only supported by GCC 3.1 and above, no IAR support + * @{ + */ +#if ((defined __GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >=1))) +#define __DEPRECATED__ __attribute__((__deprecated__)) +#else +#define __DEPRECATED__ +#endif +//! @} + +/*! \name Usual Types + */ +//! @{ +typedef unsigned char Bool; //!< Boolean. +#ifndef __cplusplus +#if !defined(__bool_true_false_are_defined) +typedef unsigned char bool; //!< Boolean. +#endif +#endif +typedef int8_t S8; //!< 8-bit signed integer. +typedef uint8_t U8; //!< 8-bit unsigned integer. +typedef int16_t S16; //!< 16-bit signed integer. +typedef uint16_t U16; //!< 16-bit unsigned integer. +typedef uint16_t le16_t; +typedef uint16_t be16_t; +typedef int32_t S32; //!< 32-bit signed integer. +typedef uint32_t U32; //!< 32-bit unsigned integer. +typedef uint32_t le32_t; +typedef uint32_t be32_t; +typedef int64_t S64; //!< 64-bit signed integer. +typedef uint64_t U64; //!< 64-bit unsigned integer. +typedef float F32; //!< 32-bit floating-point number. +typedef double F64; //!< 64-bit floating-point number. +typedef uint16_t iram_size_t; +//! @} + + +/*! \name Status Types + */ +//! @{ +typedef Bool Status_bool_t; //!< Boolean status. +typedef U8 Status_t; //!< 8-bit-coded status. +//! @} + + +/*! \name Aliasing Aggregate Types + */ +//! @{ + +//! 16-bit union. +typedef union +{ + S16 s16; + U16 u16; + S8 s8[2]; + U8 u8[2]; +} +Union16; + +//! 32-bit union. +typedef union +{ + S32 s32; + U32 u32; + S16 s16[2]; + U16 u16[2]; + S8 s8[4]; + U8 u8[4]; +} +Union32; + +//! 64-bit union. +typedef union +{ + S64 s64; + U64 u64; + S32 s32[2]; + U32 u32[2]; + S16 s16[4]; + U16 u16[4]; + S8 s8[8]; + U8 u8[8]; +} +Union64; + +//! Union of pointers to 64-, 32-, 16- and 8-bit unsigned integers. +typedef union +{ + S64 *s64ptr; + U64 *u64ptr; + S32 *s32ptr; + U32 *u32ptr; + S16 *s16ptr; + U16 *u16ptr; + S8 *s8ptr; + U8 *u8ptr; +} +UnionPtr; + +//! Union of pointers to volatile 64-, 32-, 16- and 8-bit unsigned integers. +typedef union +{ + volatile S64 *s64ptr; + volatile U64 *u64ptr; + volatile S32 *s32ptr; + volatile U32 *u32ptr; + volatile S16 *s16ptr; + volatile U16 *u16ptr; + volatile S8 *s8ptr; + volatile U8 *u8ptr; +} +UnionVPtr; + +//! Union of pointers to constant 64-, 32-, 16- and 8-bit unsigned integers. +typedef union +{ + const S64 *s64ptr; + const U64 *u64ptr; + const S32 *s32ptr; + const U32 *u32ptr; + const S16 *s16ptr; + const U16 *u16ptr; + const S8 *s8ptr; + const U8 *u8ptr; +} +UnionCPtr; + +//! Union of pointers to constant volatile 64-, 32-, 16- and 8-bit unsigned integers. +typedef union +{ + const volatile S64 *s64ptr; + const volatile U64 *u64ptr; + const volatile S32 *s32ptr; + const volatile U32 *u32ptr; + const volatile S16 *s16ptr; + const volatile U16 *u16ptr; + const volatile S8 *s8ptr; + const volatile U8 *u8ptr; +} +UnionCVPtr; + +//! Structure of pointers to 64-, 32-, 16- and 8-bit unsigned integers. +typedef struct +{ + S64 *s64ptr; + U64 *u64ptr; + S32 *s32ptr; + U32 *u32ptr; + S16 *s16ptr; + U16 *u16ptr; + S8 *s8ptr; + U8 *u8ptr; +} +StructPtr; + +//! Structure of pointers to volatile 64-, 32-, 16- and 8-bit unsigned integers. +typedef struct +{ + volatile S64 *s64ptr; + volatile U64 *u64ptr; + volatile S32 *s32ptr; + volatile U32 *u32ptr; + volatile S16 *s16ptr; + volatile U16 *u16ptr; + volatile S8 *s8ptr; + volatile U8 *u8ptr; +} +StructVPtr; + +//! Structure of pointers to constant 64-, 32-, 16- and 8-bit unsigned integers. +typedef struct +{ + const S64 *s64ptr; + const U64 *u64ptr; + const S32 *s32ptr; + const U32 *u32ptr; + const S16 *s16ptr; + const U16 *u16ptr; + const S8 *s8ptr; + const U8 *u8ptr; +} +StructCPtr; + +//! Structure of pointers to constant volatile 64-, 32-, 16- and 8-bit unsigned integers. +typedef struct +{ + const volatile S64 *s64ptr; + const volatile U64 *u64ptr; + const volatile S32 *s32ptr; + const volatile U32 *u32ptr; + const volatile S16 *s16ptr; + const volatile U16 *u16ptr; + const volatile S8 *s8ptr; + const volatile U8 *u8ptr; +} +StructCVPtr; + +//! @} + + +//_____ M A C R O S ________________________________________________________ + +/*! \name Usual Constants + */ +//! @{ +#define DISABLE 0 +#define ENABLE 1 +#ifndef __cplusplus +#if !defined(__bool_true_false_are_defined) +#define false 0 +#define true 1 +#endif +#endif +#define PASS 0 +#define FAIL 1 +#define LOW 0 +#define HIGH 1 +//! @} + + +//! \name Compile time error handling +//@{ + +/** + * \internal + * \def ERROR_FUNC(name, msg) + * \brief Fail compilation if function call isn't eliminated + * + * If the compiler fails to optimize away all calls to the function \a + * name, terminate compilation and display \a msg to the user. + * + * \note Not all compilers support this, so this is best-effort only. + * Sometimes, there may be a linker error instead, and when optimization + * is disabled, this mechanism will be completely disabled. + */ +#ifndef ERROR_FUNC +# define ERROR_FUNC(name, msg) \ + extern int name(void) +#endif + +//@} + +//! \name Function call demultiplexing +//@{ + +//! Error function for failed demultiplexing. +ERROR_FUNC (compiler_demux_bad_size, "Invalid parameter size"); + +/** + * \internal + * \brief Demultiplex function call based on size of datatype + * + * Evaluates to a function call to a function name with suffix 8, 16 or 32 + * depending on the size of the datatype. Any number of parameters can be + * passed to the function. + * + * Usage: + * \code + * void foo8(uint8_t a, void *b); + * void foo16(uint16_t a, void *b); + * void foo32(uint32_t a, void *b); + * + * #define foo(x, y) compiler_demux_size(sizeof(x), foo, x, y) + * \endcode + * + * \param size Size of the datatype. + * \param func Base function name. + * \param ... List of parameters to pass to the function. + */ +#define compiler_demux_size(size, func, ...) \ + (((size) == 1) ? func##8(__VA_ARGS__) : \ + ((size) == 2) ? func##16(__VA_ARGS__) : \ + ((size) == 4) ? func##32(__VA_ARGS__) : \ + compiler_demux_bad_size()) + +//@} + +/** + * \def __always_inline + * \brief The function should always be inlined. + * + * This annotation instructs the compiler to ignore its inlining + * heuristics and inline the function no matter how big it thinks it + * becomes. + */ +#if (defined __GNUC__) +#define __always_inline inline __attribute__((__always_inline__)) +#elif (defined __ICCAVR__) +#define __always_inline _Pragma("inline=forced") +#endif + +//! \name Optimization Control +//@{ + +/** + * \def __always_optimize + * \brief The function should always be optimized. + * + * This annotation instructs the compiler to ignore global optimization + * settings and always compile the function with a high level of + * optimization. + */ +#if (defined __GNUC__) +#define __always_optimize __attribute__((optimize(3))) +#elif (defined __ICCAVR__) +#define __always_optimize _Pragma("optimize=high") +#endif + +/** + * \def likely(exp) + * \brief The expression \a exp is likely to be true + */ +#ifndef likely +# define likely(exp) (exp) +#endif + +/** + * \def unlikely(exp) + * \brief The expression \a exp is unlikely to be true + */ +#ifndef unlikely +# define unlikely(exp) (exp) +#endif + +/** + * \def is_constant(exp) + * \brief Determine if an expression evaluates to a constant value. + * + * \param exp Any expression + * + * \return true if \a exp is constant, false otherwise. + */ +#ifdef __GNUC__ +# define is_constant(exp) __builtin_constant_p(exp) +#else +# define is_constant(exp) (0) +#endif + +//! @} + +/*! \name Bit-Field Handling + */ +#include "bit_handling/clz_ctz.h" +//! @{ + +/*! \brief Reads the bits of a value specified by a given bit-mask. + * + * \param value Value to read bits from. + * \param mask Bit-mask indicating bits to read. + * + * \return Read bits. + */ +#define Rd_bits( value, mask) ((value)&(mask)) + +/*! \brief Writes the bits of a C lvalue specified by a given bit-mask. + * + * \param lvalue C lvalue to write bits to. + * \param mask Bit-mask indicating bits to write. + * \param bits Bits to write. + * + * \return Resulting value with written bits. + */ +#define Wr_bits(lvalue, mask, bits) ((lvalue) = ((lvalue) & ~(mask)) |\ + ((bits ) & (mask))) + +/*! \brief Tests the bits of a value specified by a given bit-mask. + * + * \param value Value of which to test bits. + * \param mask Bit-mask indicating bits to test. + * + * \return \c 1 if at least one of the tested bits is set, else \c 0. + */ +#define Tst_bits( value, mask) (Rd_bits(value, mask) != 0) + +/*! \brief Clears the bits of a C lvalue specified by a given bit-mask. + * + * \param lvalue C lvalue of which to clear bits. + * \param mask Bit-mask indicating bits to clear. + * + * \return Resulting value with cleared bits. + */ +#define Clr_bits(lvalue, mask) ((lvalue) &= ~(mask)) + +/*! \brief Sets the bits of a C lvalue specified by a given bit-mask. + * + * \param lvalue C lvalue of which to set bits. + * \param mask Bit-mask indicating bits to set. + * + * \return Resulting value with set bits. + */ +#define Set_bits(lvalue, mask) ((lvalue) |= (mask)) + +/*! \brief Toggles the bits of a C lvalue specified by a given bit-mask. + * + * \param lvalue C lvalue of which to toggle bits. + * \param mask Bit-mask indicating bits to toggle. + * + * \return Resulting value with toggled bits. + */ +#define Tgl_bits(lvalue, mask) ((lvalue) ^= (mask)) + +/*! \brief Reads the bit-field of a value specified by a given bit-mask. + * + * \param value Value to read a bit-field from. + * \param mask Bit-mask indicating the bit-field to read. + * + * \return Read bit-field. + */ +#define Rd_bitfield( value,mask) (Rd_bits( value, (uint32_t)mask) >> ctz(mask)) + +/*! \brief Writes the bit-field of a C lvalue specified by a given bit-mask. + * + * \param lvalue C lvalue to write a bit-field to. + * \param mask Bit-mask indicating the bit-field to write. + * \param bitfield Bit-field to write. + * + * \return Resulting value with written bit-field. + */ +#define Wr_bitfield(lvalue, mask, bitfield) (Wr_bits(lvalue, mask, (uint32_t)(bitfield) << ctz(mask))) + +//! @} + + +/*! \brief This macro is used to test fatal errors. + * + * The macro tests if the expression is false. If it is, a fatal error is + * detected and the application hangs up. If TEST_SUITE_DEFINE_ASSERT_MACRO + * is defined, a unit test version of the macro is used, to allow execution + * of further tests after a false expression. + * + * \param expr Expression to evaluate and supposed to be nonzero. + */ +#if defined(_ASSERT_ENABLE_) +# if defined(TEST_SUITE_DEFINE_ASSERT_MACRO) + // Assert() is defined in unit_test/suite.h +# include "unit_test/suite.h" +# else +# define Assert(expr) \ + {\ + if (!(expr)) while (true);\ + } +# endif +#else +# define Assert(expr) ((void) 0) +#endif + +/*! \name Bit Reversing + */ +//! @{ + +/*! \brief Reverses the bits of \a u8. + * + * \param u8 U8 of which to reverse the bits. + * + * \return Value resulting from \a u8 with reversed bits. + */ +#define bit_reverse8(u8) ((U8)(bit_reverse32((U8)(u8)) >> 24)) + +/*! \brief Reverses the bits of \a u16. + * + * \param u16 U16 of which to reverse the bits. + * + * \return Value resulting from \a u16 with reversed bits. + */ +#define bit_reverse16(u16) ((U16)(bit_reverse32((U16)(u16)) >> 16)) + +/*! \brief Reverses the bits of \a u32. + * + * \param u32 U32 of which to reverse the bits. + * + * \return Value resulting from \a u32 with reversed bits. + */ +#if (defined __GNUC__) +#define bit_reverse32(u32) \ + (\ + {\ + unsigned int __value = (U32)(u32);\ + __asm__ ("brev\t%0" : "+r" (__value) : : "cc");\ + (U32)__value;\ + }\ + ) +#elif (defined __ICCAVR__) +#define bit_reverse32(u32) ((U32)__bit_reverse((U32)(u32))) +#endif + +/*! \brief Reverses the bits of \a u64. + * + * \param u64 U64 of which to reverse the bits. + * + * \return Value resulting from \a u64 with reversed bits. + */ +#define bit_reverse64(u64) ((U64)(((U64)bit_reverse32((U64)(u64) >> 32)) |\ + ((U64)bit_reverse32((U64)(u64)) << 32))) + +//! @} + +//! \name Logarithmic functions +//! @{ + +/** + * \internal + * Undefined function. Will cause a link failure if ilog2() is called + * with an invalid constant value. + */ +int_fast8_t ilog2_undefined (void); + +/** + * \brief Calculate the base-2 logarithm of a number rounded down to + * the nearest integer. + * + * \param x A 32-bit value + * \return The base-2 logarithm of \a x, or -1 if \a x is 0. + */ +static inline int_fast8_t +ilog2 (uint32_t x) +{ + if (is_constant (x)) + return ((x) & (1ULL << 31) ? 31 : + (x) & (1ULL << 30) ? 30 : + (x) & (1ULL << 29) ? 29 : + (x) & (1ULL << 28) ? 28 : + (x) & (1ULL << 27) ? 27 : + (x) & (1ULL << 26) ? 26 : + (x) & (1ULL << 25) ? 25 : + (x) & (1ULL << 24) ? 24 : + (x) & (1ULL << 23) ? 23 : + (x) & (1ULL << 22) ? 22 : + (x) & (1ULL << 21) ? 21 : + (x) & (1ULL << 20) ? 20 : + (x) & (1ULL << 19) ? 19 : + (x) & (1ULL << 18) ? 18 : + (x) & (1ULL << 17) ? 17 : + (x) & (1ULL << 16) ? 16 : + (x) & (1ULL << 15) ? 15 : + (x) & (1ULL << 14) ? 14 : + (x) & (1ULL << 13) ? 13 : + (x) & (1ULL << 12) ? 12 : + (x) & (1ULL << 11) ? 11 : + (x) & (1ULL << 10) ? 10 : + (x) & (1ULL << 9) ? 9 : + (x) & (1ULL << 8) ? 8 : + (x) & (1ULL << 7) ? 7 : + (x) & (1ULL << 6) ? 6 : + (x) & (1ULL << 5) ? 5 : + (x) & (1ULL << 4) ? 4 : + (x) & (1ULL << 3) ? 3 : + (x) & (1ULL << 2) ? 2 : + (x) & (1ULL << 1) ? 1 : + (x) & (1ULL << 0) ? 0 : ilog2_undefined ()); + + return 31 - clz (x); +} + +//! @} + +/*! \name Alignment + */ +//! @{ + +/*! \brief Tests alignment of the number \a val with the \a n boundary. + * + * \param val Input value. + * \param n Boundary. + * + * \return \c 1 if the number \a val is aligned with the \a n boundary, else \c 0. + */ +#define Test_align(val, n ) (!Tst_bits( val, (n) - 1 ) ) + +/*! \brief Gets alignment of the number \a val with respect to the \a n boundary. + * + * \param val Input value. + * \param n Boundary. + * + * \return Alignment of the number \a val with respect to the \a n boundary. + */ +#define Get_align( val, n ) ( Rd_bits( val, (n) - 1 ) ) + +/*! \brief Sets alignment of the lvalue number \a lval to \a alg with respect to the \a n boundary. + * + * \param lval Input/output lvalue. + * \param n Boundary. + * \param alg Alignment. + * + * \return New value of \a lval resulting from its alignment set to \a alg with respect to the \a n boundary. + */ +#define Set_align(lval, n, alg) ( Wr_bits(lval, (n) - 1, alg) ) + +/*! \brief Aligns the number \a val with the upper \a n boundary. + * + * \param val Input value. + * \param n Boundary. + * + * \return Value resulting from the number \a val aligned with the upper \a n boundary. + */ +#define Align_up( val, n ) (((val) + ((n) - 1)) & ~((n) - 1)) + +/*! \brief Aligns the number \a val with the lower \a n boundary. + * + * \param val Input value. + * \param n Boundary. + * + * \return Value resulting from the number \a val aligned with the lower \a n boundary. + */ +#define Align_down(val, n ) ( (val) & ~((n) - 1)) + +//! @} + + +/*! \name Mathematics + * + * Compiler optimization for non-constant expressions, only for abs under WinAVR + */ +//! @{ + +/*! \brief Takes the absolute value of \a a. + * + * \param a Input value. + * + * \return Absolute value of \a a. + * + * \note More optimized if only used with values known at compile time. + */ +#define Abs(a) (((a) < 0 ) ? -(a) : (a)) +#ifndef abs +#define abs(a) Abs(a) +#endif + +/*! \brief Takes the minimal value of \a a and \a b. + * + * \param a Input value. + * \param b Input value. + * + * \return Minimal value of \a a and \a b. + * + * \note More optimized if only used with values known at compile time. + */ +#define Min(a, b) (((a) < (b)) ? (a) : (b)) +#define min(a, b) Min(a, b) + +/*! \brief Takes the maximal value of \a a and \a b. + * + * \param a Input value. + * \param b Input value. + * + * \return Maximal value of \a a and \a b. + * + * \note More optimized if only used with values known at compile time. + */ +#define Max(a, b) (((a) > (b)) ? (a) : (b)) +#define max(a, b) Max(a, b) + +//! @} + + +/*! \brief Calls the routine at address \a addr. + * + * It generates a long call opcode. + * + * For example, `Long_call(0x80000000)' generates a software reset on a UC3 if + * it is invoked from the CPU supervisor mode. + * + * \param addr Address of the routine to call. + * + * \note It may be used as a long jump opcode in some special cases. + */ +#define Long_call(addr) ((*(void (*)(void))(addr))()) + +/*! \name System Register Access + */ +//! @{ + +/*! \brief Gets the value of the \a sysreg system register. + * + * \param sysreg Address of the system register of which to get the value. + * + * \return Value of the \a sysreg system register. + */ +#if (defined __GNUC__) +#define Get_system_register(sysreg) __builtin_mfsr(sysreg) +#elif (defined __ICCAVR__) +#define Get_system_register(sysreg) __get_system_register(sysreg) +#endif + +/*! \brief Sets the value of the \a sysreg system register to \a value. + * + * \param sysreg Address of the system register of which to set the value. + * \param value Value to set the \a sysreg system register to. + */ +#if (defined __GNUC__) +#define Set_system_register(sysreg, value) __builtin_mtsr(sysreg, value) +#elif (defined __ICCAVR__) +#define Set_system_register(sysreg, value) __set_system_register(sysreg, value) +#endif + +//! @} + +/*! \name Debug Register Access + */ +//! @{ + +/*! \brief Gets the value of the \a dbgreg debug register. + * + * \param dbgreg Address of the debug register of which to get the value. + * + * \return Value of the \a dbgreg debug register. + */ +#if (defined __GNUC__) +#define Get_debug_register(dbgreg) __builtin_mfdr(dbgreg) +#elif (defined __ICCAVR__) +#define Get_debug_register(dbgreg) __get_debug_register(dbgreg) +#endif + +/*! \brief Sets the value of the \a dbgreg debug register to \a value. + * + * \param dbgreg Address of the debug register of which to set the value. + * \param value Value to set the \a dbgreg debug register to. + */ +#if (defined __GNUC__) +#define Set_debug_register(dbgreg, value) __builtin_mtdr(dbgreg, value) +#elif (defined __ICCAVR__) +#define Set_debug_register(dbgreg, value) __set_debug_register(dbgreg, value) +#endif + +//! @} + + +/*! \name MCU Endianism Handling + * xmega is a MCU little endianism. + */ +//! @{ +#define MSB(u16) (((uint8_t* )&u16)[1]) +#define LSB(u16) (((uint8_t* )&u16)[0]) + +#define MSW(u32) (((uint16_t*)&u32)[1]) +#define LSW(u32) (((uint16_t*)&u32)[0]) +#define MSB0W(u32) (((uint8_t*)&(u32))[3]) //!< Most significant byte of 1st rank of \a u32. +#define MSB1W(u32) (((uint8_t*)&(u32))[2]) //!< Most significant byte of 2nd rank of \a u32. +#define MSB2W(u32) (((uint8_t*)&(u32))[1]) //!< Most significant byte of 3rd rank of \a u32. +#define MSB3W(u32) (((uint8_t*)&(u32))[0]) //!< Most significant byte of 4th rank of \a u32. +#define LSB3W(u32) MSB0W(u32) //!< Least significant byte of 4th rank of \a u32. +#define LSB2W(u32) MSB1W(u32) //!< Least significant byte of 3rd rank of \a u32. +#define LSB1W(u32) MSB2W(u32) //!< Least significant byte of 2nd rank of \a u32. +#define LSB0W(u32) MSB3W(u32) //!< Least significant byte of 1st rank of \a u32. + +#define MSB0(u32) (((uint8_t*)&u32)[3]) +#define MSB1(u32) (((uint8_t*)&u32)[2]) +#define MSB2(u32) (((uint8_t*)&u32)[1]) +#define MSB3(u32) (((uint8_t*)&u32)[0]) +#define LSB0(u32) MSB3(u32) +#define LSB1(u32) MSB2(u32) +#define LSB2(u32) MSB1(u32) +#define LSB3(u32) MSB0(u32) + +#define LE16(x) (x) +#define le16_to_cpu(x) (x) +#define cpu_to_le16(x) (x) +#define LE16_TO_CPU(x) (x) +#define CPU_TO_LE16(x) (x) + +#define BE16(x) Swap16(x) +#define be16_to_cpu(x) swap16(x) +#define cpu_to_be16(x) swap16(x) +#define BE16_TO_CPU(x) Swap16(x) +#define CPU_TO_BE16(x) Swap16(x) + +#define LE32(x) (x) +#define le32_to_cpu(x) (x) +#define cpu_to_le32(x) (x) +#define LE32_TO_CPU(x) (x) +#define CPU_TO_LE32(x) (x) + +#define BE32(x) Swap32(x) +#define be32_to_cpu(x) swap32(x) +#define cpu_to_be32(x) swap32(x) +#define BE32_TO_CPU(x) Swap32(x) +#define CPU_TO_BE32(x) Swap32(x) + + + +//! @} + + +/*! \name Endianism Conversion + * + * The same considerations as for clz and ctz apply here but AVR32-GCC's + * __builtin_bswap_16 and __builtin_bswap_32 do not behave like macros when + * applied to constant expressions, so two sets of macros are defined here: + * - Swap16, Swap32 and Swap64 to apply to constant expressions (values known + * at compile time); + * - swap16, swap32 and swap64 to apply to non-constant expressions (values + * unknown at compile time). + */ +//! @{ + +/*! \brief Toggles the endianism of \a u16 (by swapping its bytes). + * + * \param u16 U16 of which to toggle the endianism. + * + * \return Value resulting from \a u16 with toggled endianism. + * + * \note More optimized if only used with values known at compile time. + */ +#define Swap16(u16) ((U16)(((U16)(u16) >> 8) |\ + ((U16)(u16) << 8))) + +/*! \brief Toggles the endianism of \a u32 (by swapping its bytes). + * + * \param u32 U32 of which to toggle the endianism. + * + * \return Value resulting from \a u32 with toggled endianism. + * + * \note More optimized if only used with values known at compile time. + */ +#define Swap32(u32) ((U32)(((U32)Swap16((U32)(u32) >> 16)) |\ + ((U32)Swap16((U32)(u32)) << 16))) + +/*! \brief Toggles the endianism of \a u64 (by swapping its bytes). + * + * \param u64 U64 of which to toggle the endianism. + * + * \return Value resulting from \a u64 with toggled endianism. + * + * \note More optimized if only used with values known at compile time. + */ +#define Swap64(u64) ((U64)(((U64)Swap32((U64)(u64) >> 32)) |\ + ((U64)Swap32((U64)(u64)) << 32))) + +/*! \brief Toggles the endianism of \a u16 (by swapping its bytes). + * + * \param u16 U16 of which to toggle the endianism. + * + * \return Value resulting from \a u16 with toggled endianism. + * + * \note More optimized if only used with values unknown at compile time. + */ +#define swap16(u16) Swap16(u16) + +/*! \brief Toggles the endianism of \a u32 (by swapping its bytes). + * + * \param u32 U32 of which to toggle the endianism. + * + * \return Value resulting from \a u32 with toggled endianism. + * + * \note More optimized if only used with values unknown at compile time. + */ +#define swap32(u32) Swap32(u32) + +/*! \brief Toggles the endianism of \a u64 (by swapping its bytes). + * + * \param u64 U64 of which to toggle the endianism. + * + * \return Value resulting from \a u64 with toggled endianism. + * + * \note More optimized if only used with values unknown at compile time. + */ +#define swap64(u64) ((U64)(((U64)swap32((U64)(u64) >> 32)) |\ + ((U64)swap32((U64)(u64)) << 32))) + +//! @} + + +/*! \name Target Abstraction + */ +//! @{ + +#define _GLOBEXT_ extern //!< extern storage-class specifier. +#define _CONST_TYPE_ const //!< const type qualifier. +#define _MEM_TYPE_SLOW_ //!< Slow memory type. +#define _MEM_TYPE_MEDFAST_ //!< Fairly fast memory type. +#define _MEM_TYPE_FAST_ //!< Fast memory type. + +typedef U8 Byte; //!< 8-bit unsigned integer. + +#define memcmp_ram2ram memcmp //!< Target-specific memcmp of RAM to RAM. +#define memcmp_code2ram memcmp //!< Target-specific memcmp of RAM to NVRAM. +#define memcpy_ram2ram memcpy //!< Target-specific memcpy from RAM to RAM. +#define memcpy_code2ram memcpy //!< Target-specific memcpy from NVRAM to RAM. + +//! @} + +/** + * \brief Calculate \f$ \left\lceil \frac{a}{b} \right\rceil \f$ using + * integer arithmetic. + * + * \param a An integer + * \param b Another integer + * + * \return (\a a / \a b) rounded up to the nearest integer. + */ +#define div_ceil(a, b) (((a) + (b) - 1) / (b)) + +#include "preprocessor.h" +#include "progmem.h" +#include "interrupt.h" + + +#if (defined __GNUC__) +#define SHORTENUM __attribute__ ((packed)) +#elif (defined __ICCAVR__) +#define SHORTENUM /**/ +#endif +#if (defined __GNUC__) +#define FUNC_PTR void * +#elif (defined __ICCAVR__) +#if (FLASHEND > 0x1FFFF) // Required for program code larger than 128K +#define FUNC_PTR void __farflash * +#else +#define FUNC_PTR void * +#endif /* ENABLE_FAR_FLASH */ +#endif +#if (defined __GNUC__) +#define FLASH_DECLARE(x) const x __attribute__((__progmem__)) +#elif (defined __ICCAVR__) +#define FLASH_DECLARE(x) const __flash x +#endif +#if (defined __GNUC__) +#define FLASH_EXTERN(x) extern const x +#elif (defined __ICCAVR__) +#define FLASH_EXTERN(x) extern const __flash x +#endif +/*Defines the Flash Storage for the request and response of MAC*/ +#define CMD_ID_OCTET (0) +/* Converting of values from CPU endian to little endian. */ +#define CPU_ENDIAN_TO_LE16(x) (x) +#define CPU_ENDIAN_TO_LE32(x) (x) +#define CPU_ENDIAN_TO_LE64(x) (x) +/* Converting of values from little endian to CPU endian. */ +#define LE16_TO_CPU_ENDIAN(x) (x) +#define LE32_TO_CPU_ENDIAN(x) (x) +#define LE64_TO_CPU_ENDIAN(x) (x) +/* Converting of constants from little endian to CPU endian. */ +#define CLE16_TO_CPU_ENDIAN(x) (x) +#define CLE32_TO_CPU_ENDIAN(x) (x) +#define CLE64_TO_CPU_ENDIAN(x) (x) +/* Converting of constants from CPU endian to little endian. */ +#define CCPU_ENDIAN_TO_LE16(x) (x) +#define CCPU_ENDIAN_TO_LE32(x) (x) +#define CCPU_ENDIAN_TO_LE64(x) (x) +#if (defined __GNUC__) +#define ADDR_COPY_DST_SRC_16(dst, src) memcpy((&(dst)), (&(src)), sizeof(uint16_t)) +#define ADDR_COPY_DST_SRC_64(dst, src) memcpy((&(dst)), (&(src)), sizeof(uint64_t)) +/* Converts a 2 Byte array into a 16-Bit value */ +#define convert_byte_array_to_16_bit(data) \ + (*(uint16_t *)(data)) +/* Converts a 4 Byte array into a 32-Bit value */ +#define convert_byte_array_to_32_bit(data) \ + (*(uint32_t *)(data)) +/* Converts a 8 Byte array into a 64-Bit value */ +#define convert_byte_array_to_64_bit(data) \ + (*(uint64_t *)(data)) +/* Converts a 16-Bit value into a 2 Byte array */ +#define convert_16_bit_to_byte_array(value, data) \ + ((*(uint16_t *)(data)) = (uint16_t)(value)) +/* Converts spec 16-Bit value into a 2 Byte array */ +#define convert_spec_16_bit_to_byte_array(value, data) \ + ((*(uint16_t *)(data)) = (uint16_t)(value)) +/* Converts spec 16-Bit value into a 2 Byte array */ +#define convert_16_bit_to_byte_address(value, data) \ + ((*(uint16_t *)(data)) = (uint16_t)(value)) +/* Converts a 32-Bit value into a 4 Byte array */ +#define convert_32_bit_to_byte_array(value, data) \ + ((*(uint32_t *)(data)) = (uint32_t)(value)) +/* Converts a 64-Bit value into a 8 Byte array */ +/* Here memcpy requires much less footprint */ +#define convert_64_bit_to_byte_array(value, data) \ + memcpy((data), (&(value)), sizeof(uint64_t)) +#elif (defined __ICCAVR__) +#define ADDR_COPY_DST_SRC_16(dst, src) ((dst) = (src)) +#define ADDR_COPY_DST_SRC_64(dst, src) ((dst) = (src)) +/* Converts a 2 Byte array into a 16-Bit value */ +#define convert_byte_array_to_16_bit(data) \ + (*(uint16_t *)(data)) +/* Converts a 4 Byte array into a 32-Bit value */ +#define convert_byte_array_to_32_bit(data) \ + (*(uint32_t *)(data)) +/* Converts a 8 Byte array into a 64-Bit value */ +#define convert_byte_array_to_64_bit(data) \ + (*(uint64_t *)(data)) +/* Converts a 16-Bit value into a 2 Byte array */ +#define convert_16_bit_to_byte_array(value, data) \ + ((*(uint16_t *)(data)) = (uint16_t)(value)) +/* Converts spec 16-Bit value into a 2 Byte array */ +#define convert_spec_16_bit_to_byte_array(value, data) \ + ((*(uint16_t *)(data)) = (uint16_t)(value)) +/* Converts spec 16-Bit value into a 2 Byte array */ +#define convert_16_bit_to_byte_address(value, data) \ + ((*(uint16_t *)(data)) = (uint16_t)(value)) +/* Converts a 32-Bit value into a 4 Byte array */ +#define convert_32_bit_to_byte_array(value, data) \ + ((*(uint32_t *)(data)) = (uint32_t)(value)) +/* Converts a 64-Bit value into a 8 Byte array */ +#define convert_64_bit_to_byte_array(value, data) \ + ((*(uint64_t *)(data)) = (uint64_t)(value)) +#endif +#define MEMCPY_ENDIAN memcpy +#define PGM_READ_BLOCK(dst, src, len) memcpy_P((dst), (src), (len)) +#if (defined __GNUC__) +#define PGM_READ_BYTE(x) pgm_read_byte(x) +#define PGM_READ_WORD(x) pgm_read_word(x) +#elif (defined __ICCAVR__) +#define PGM_READ_BYTE(x) *(x) +#define PGM_READ_WORD(x) *(x) +#endif +#if (defined __GNUC__) +#define nop() do { __asm__ __volatile__ ("nop"); } while (0) +#elif (defined __ICCAVR__) +#define nop() __no_operation() +#endif +/** + * \} + */ +#endif // UTILS_COMPILER_H diff --git a/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/mrepeat.h b/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/mrepeat.h new file mode 100644 index 00000000..42647ef7 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/mrepeat.h @@ -0,0 +1,335 @@ +/** + * \file + * + * \brief Preprocessor macro repeating utils. + * + * Copyright (c) 2009 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _MREPEAT_H_ +#define _MREPEAT_H_ + +/** + * \defgroup group_xmega_utils_mrepeat Macro Repeat + * + * \ingroup group_xmega_utils + * + * \{ + */ + +#include "preprocessor.h" + + +//! Maximal number of repetitions supported by MREPEAT. +#define MREPEAT_LIMIT 256 + +/*! \brief Macro repeat. + * + * This macro represents a horizontal repetition construct. + * + * \param count The number of repetitious calls to macro. Valid values range from 0 to MREPEAT_LIMIT. + * \param macro A binary operation of the form macro(n, data). This macro is expanded by MREPEAT with + * the current repetition number and the auxiliary data argument. + * \param data Auxiliary data passed to macro. + * + * \return macro(0, data) macro(1, data) ... macro(count - 1, data) + */ +#define MREPEAT(count, macro, data) TPASTE2(MREPEAT, count)(macro, data) + +#define MREPEAT0( macro, data) +#define MREPEAT1( macro, data) MREPEAT0( macro, data) macro( 0, data) +#define MREPEAT2( macro, data) MREPEAT1( macro, data) macro( 1, data) +#define MREPEAT3( macro, data) MREPEAT2( macro, data) macro( 2, data) +#define MREPEAT4( macro, data) MREPEAT3( macro, data) macro( 3, data) +#define MREPEAT5( macro, data) MREPEAT4( macro, data) macro( 4, data) +#define MREPEAT6( macro, data) MREPEAT5( macro, data) macro( 5, data) +#define MREPEAT7( macro, data) MREPEAT6( macro, data) macro( 6, data) +#define MREPEAT8( macro, data) MREPEAT7( macro, data) macro( 7, data) +#define MREPEAT9( macro, data) MREPEAT8( macro, data) macro( 8, data) +#define MREPEAT10( macro, data) MREPEAT9( macro, data) macro( 9, data) +#define MREPEAT11( macro, data) MREPEAT10( macro, data) macro( 10, data) +#define MREPEAT12( macro, data) MREPEAT11( macro, data) macro( 11, data) +#define MREPEAT13( macro, data) MREPEAT12( macro, data) macro( 12, data) +#define MREPEAT14( macro, data) MREPEAT13( macro, data) macro( 13, data) +#define MREPEAT15( macro, data) MREPEAT14( macro, data) macro( 14, data) +#define MREPEAT16( macro, data) MREPEAT15( macro, data) macro( 15, data) +#define MREPEAT17( macro, data) MREPEAT16( macro, data) macro( 16, data) +#define MREPEAT18( macro, data) MREPEAT17( macro, data) macro( 17, data) +#define MREPEAT19( macro, data) MREPEAT18( macro, data) macro( 18, data) +#define MREPEAT20( macro, data) MREPEAT19( macro, data) macro( 19, data) +#define MREPEAT21( macro, data) MREPEAT20( macro, data) macro( 20, data) +#define MREPEAT22( macro, data) MREPEAT21( macro, data) macro( 21, data) +#define MREPEAT23( macro, data) MREPEAT22( macro, data) macro( 22, data) +#define MREPEAT24( macro, data) MREPEAT23( macro, data) macro( 23, data) +#define MREPEAT25( macro, data) MREPEAT24( macro, data) macro( 24, data) +#define MREPEAT26( macro, data) MREPEAT25( macro, data) macro( 25, data) +#define MREPEAT27( macro, data) MREPEAT26( macro, data) macro( 26, data) +#define MREPEAT28( macro, data) MREPEAT27( macro, data) macro( 27, data) +#define MREPEAT29( macro, data) MREPEAT28( macro, data) macro( 28, data) +#define MREPEAT30( macro, data) MREPEAT29( macro, data) macro( 29, data) +#define MREPEAT31( macro, data) MREPEAT30( macro, data) macro( 30, data) +#define MREPEAT32( macro, data) MREPEAT31( macro, data) macro( 31, data) +#define MREPEAT33( macro, data) MREPEAT32( macro, data) macro( 32, data) +#define MREPEAT34( macro, data) MREPEAT33( macro, data) macro( 33, data) +#define MREPEAT35( macro, data) MREPEAT34( macro, data) macro( 34, data) +#define MREPEAT36( macro, data) MREPEAT35( macro, data) macro( 35, data) +#define MREPEAT37( macro, data) MREPEAT36( macro, data) macro( 36, data) +#define MREPEAT38( macro, data) MREPEAT37( macro, data) macro( 37, data) +#define MREPEAT39( macro, data) MREPEAT38( macro, data) macro( 38, data) +#define MREPEAT40( macro, data) MREPEAT39( macro, data) macro( 39, data) +#define MREPEAT41( macro, data) MREPEAT40( macro, data) macro( 40, data) +#define MREPEAT42( macro, data) MREPEAT41( macro, data) macro( 41, data) +#define MREPEAT43( macro, data) MREPEAT42( macro, data) macro( 42, data) +#define MREPEAT44( macro, data) MREPEAT43( macro, data) macro( 43, data) +#define MREPEAT45( macro, data) MREPEAT44( macro, data) macro( 44, data) +#define MREPEAT46( macro, data) MREPEAT45( macro, data) macro( 45, data) +#define MREPEAT47( macro, data) MREPEAT46( macro, data) macro( 46, data) +#define MREPEAT48( macro, data) MREPEAT47( macro, data) macro( 47, data) +#define MREPEAT49( macro, data) MREPEAT48( macro, data) macro( 48, data) +#define MREPEAT50( macro, data) MREPEAT49( macro, data) macro( 49, data) +#define MREPEAT51( macro, data) MREPEAT50( macro, data) macro( 50, data) +#define MREPEAT52( macro, data) MREPEAT51( macro, data) macro( 51, data) +#define MREPEAT53( macro, data) MREPEAT52( macro, data) macro( 52, data) +#define MREPEAT54( macro, data) MREPEAT53( macro, data) macro( 53, data) +#define MREPEAT55( macro, data) MREPEAT54( macro, data) macro( 54, data) +#define MREPEAT56( macro, data) MREPEAT55( macro, data) macro( 55, data) +#define MREPEAT57( macro, data) MREPEAT56( macro, data) macro( 56, data) +#define MREPEAT58( macro, data) MREPEAT57( macro, data) macro( 57, data) +#define MREPEAT59( macro, data) MREPEAT58( macro, data) macro( 58, data) +#define MREPEAT60( macro, data) MREPEAT59( macro, data) macro( 59, data) +#define MREPEAT61( macro, data) MREPEAT60( macro, data) macro( 60, data) +#define MREPEAT62( macro, data) MREPEAT61( macro, data) macro( 61, data) +#define MREPEAT63( macro, data) MREPEAT62( macro, data) macro( 62, data) +#define MREPEAT64( macro, data) MREPEAT63( macro, data) macro( 63, data) +#define MREPEAT65( macro, data) MREPEAT64( macro, data) macro( 64, data) +#define MREPEAT66( macro, data) MREPEAT65( macro, data) macro( 65, data) +#define MREPEAT67( macro, data) MREPEAT66( macro, data) macro( 66, data) +#define MREPEAT68( macro, data) MREPEAT67( macro, data) macro( 67, data) +#define MREPEAT69( macro, data) MREPEAT68( macro, data) macro( 68, data) +#define MREPEAT70( macro, data) MREPEAT69( macro, data) macro( 69, data) +#define MREPEAT71( macro, data) MREPEAT70( macro, data) macro( 70, data) +#define MREPEAT72( macro, data) MREPEAT71( macro, data) macro( 71, data) +#define MREPEAT73( macro, data) MREPEAT72( macro, data) macro( 72, data) +#define MREPEAT74( macro, data) MREPEAT73( macro, data) macro( 73, data) +#define MREPEAT75( macro, data) MREPEAT74( macro, data) macro( 74, data) +#define MREPEAT76( macro, data) MREPEAT75( macro, data) macro( 75, data) +#define MREPEAT77( macro, data) MREPEAT76( macro, data) macro( 76, data) +#define MREPEAT78( macro, data) MREPEAT77( macro, data) macro( 77, data) +#define MREPEAT79( macro, data) MREPEAT78( macro, data) macro( 78, data) +#define MREPEAT80( macro, data) MREPEAT79( macro, data) macro( 79, data) +#define MREPEAT81( macro, data) MREPEAT80( macro, data) macro( 80, data) +#define MREPEAT82( macro, data) MREPEAT81( macro, data) macro( 81, data) +#define MREPEAT83( macro, data) MREPEAT82( macro, data) macro( 82, data) +#define MREPEAT84( macro, data) MREPEAT83( macro, data) macro( 83, data) +#define MREPEAT85( macro, data) MREPEAT84( macro, data) macro( 84, data) +#define MREPEAT86( macro, data) MREPEAT85( macro, data) macro( 85, data) +#define MREPEAT87( macro, data) MREPEAT86( macro, data) macro( 86, data) +#define MREPEAT88( macro, data) MREPEAT87( macro, data) macro( 87, data) +#define MREPEAT89( macro, data) MREPEAT88( macro, data) macro( 88, data) +#define MREPEAT90( macro, data) MREPEAT89( macro, data) macro( 89, data) +#define MREPEAT91( macro, data) MREPEAT90( macro, data) macro( 90, data) +#define MREPEAT92( macro, data) MREPEAT91( macro, data) macro( 91, data) +#define MREPEAT93( macro, data) MREPEAT92( macro, data) macro( 92, data) +#define MREPEAT94( macro, data) MREPEAT93( macro, data) macro( 93, data) +#define MREPEAT95( macro, data) MREPEAT94( macro, data) macro( 94, data) +#define MREPEAT96( macro, data) MREPEAT95( macro, data) macro( 95, data) +#define MREPEAT97( macro, data) MREPEAT96( macro, data) macro( 96, data) +#define MREPEAT98( macro, data) MREPEAT97( macro, data) macro( 97, data) +#define MREPEAT99( macro, data) MREPEAT98( macro, data) macro( 98, data) +#define MREPEAT100(macro, data) MREPEAT99( macro, data) macro( 99, data) +#define MREPEAT101(macro, data) MREPEAT100(macro, data) macro(100, data) +#define MREPEAT102(macro, data) MREPEAT101(macro, data) macro(101, data) +#define MREPEAT103(macro, data) MREPEAT102(macro, data) macro(102, data) +#define MREPEAT104(macro, data) MREPEAT103(macro, data) macro(103, data) +#define MREPEAT105(macro, data) MREPEAT104(macro, data) macro(104, data) +#define MREPEAT106(macro, data) MREPEAT105(macro, data) macro(105, data) +#define MREPEAT107(macro, data) MREPEAT106(macro, data) macro(106, data) +#define MREPEAT108(macro, data) MREPEAT107(macro, data) macro(107, data) +#define MREPEAT109(macro, data) MREPEAT108(macro, data) macro(108, data) +#define MREPEAT110(macro, data) MREPEAT109(macro, data) macro(109, data) +#define MREPEAT111(macro, data) MREPEAT110(macro, data) macro(110, data) +#define MREPEAT112(macro, data) MREPEAT111(macro, data) macro(111, data) +#define MREPEAT113(macro, data) MREPEAT112(macro, data) macro(112, data) +#define MREPEAT114(macro, data) MREPEAT113(macro, data) macro(113, data) +#define MREPEAT115(macro, data) MREPEAT114(macro, data) macro(114, data) +#define MREPEAT116(macro, data) MREPEAT115(macro, data) macro(115, data) +#define MREPEAT117(macro, data) MREPEAT116(macro, data) macro(116, data) +#define MREPEAT118(macro, data) MREPEAT117(macro, data) macro(117, data) +#define MREPEAT119(macro, data) MREPEAT118(macro, data) macro(118, data) +#define MREPEAT120(macro, data) MREPEAT119(macro, data) macro(119, data) +#define MREPEAT121(macro, data) MREPEAT120(macro, data) macro(120, data) +#define MREPEAT122(macro, data) MREPEAT121(macro, data) macro(121, data) +#define MREPEAT123(macro, data) MREPEAT122(macro, data) macro(122, data) +#define MREPEAT124(macro, data) MREPEAT123(macro, data) macro(123, data) +#define MREPEAT125(macro, data) MREPEAT124(macro, data) macro(124, data) +#define MREPEAT126(macro, data) MREPEAT125(macro, data) macro(125, data) +#define MREPEAT127(macro, data) MREPEAT126(macro, data) macro(126, data) +#define MREPEAT128(macro, data) MREPEAT127(macro, data) macro(127, data) +#define MREPEAT129(macro, data) MREPEAT128(macro, data) macro(128, data) +#define MREPEAT130(macro, data) MREPEAT129(macro, data) macro(129, data) +#define MREPEAT131(macro, data) MREPEAT130(macro, data) macro(130, data) +#define MREPEAT132(macro, data) MREPEAT131(macro, data) macro(131, data) +#define MREPEAT133(macro, data) MREPEAT132(macro, data) macro(132, data) +#define MREPEAT134(macro, data) MREPEAT133(macro, data) macro(133, data) +#define MREPEAT135(macro, data) MREPEAT134(macro, data) macro(134, data) +#define MREPEAT136(macro, data) MREPEAT135(macro, data) macro(135, data) +#define MREPEAT137(macro, data) MREPEAT136(macro, data) macro(136, data) +#define MREPEAT138(macro, data) MREPEAT137(macro, data) macro(137, data) +#define MREPEAT139(macro, data) MREPEAT138(macro, data) macro(138, data) +#define MREPEAT140(macro, data) MREPEAT139(macro, data) macro(139, data) +#define MREPEAT141(macro, data) MREPEAT140(macro, data) macro(140, data) +#define MREPEAT142(macro, data) MREPEAT141(macro, data) macro(141, data) +#define MREPEAT143(macro, data) MREPEAT142(macro, data) macro(142, data) +#define MREPEAT144(macro, data) MREPEAT143(macro, data) macro(143, data) +#define MREPEAT145(macro, data) MREPEAT144(macro, data) macro(144, data) +#define MREPEAT146(macro, data) MREPEAT145(macro, data) macro(145, data) +#define MREPEAT147(macro, data) MREPEAT146(macro, data) macro(146, data) +#define MREPEAT148(macro, data) MREPEAT147(macro, data) macro(147, data) +#define MREPEAT149(macro, data) MREPEAT148(macro, data) macro(148, data) +#define MREPEAT150(macro, data) MREPEAT149(macro, data) macro(149, data) +#define MREPEAT151(macro, data) MREPEAT150(macro, data) macro(150, data) +#define MREPEAT152(macro, data) MREPEAT151(macro, data) macro(151, data) +#define MREPEAT153(macro, data) MREPEAT152(macro, data) macro(152, data) +#define MREPEAT154(macro, data) MREPEAT153(macro, data) macro(153, data) +#define MREPEAT155(macro, data) MREPEAT154(macro, data) macro(154, data) +#define MREPEAT156(macro, data) MREPEAT155(macro, data) macro(155, data) +#define MREPEAT157(macro, data) MREPEAT156(macro, data) macro(156, data) +#define MREPEAT158(macro, data) MREPEAT157(macro, data) macro(157, data) +#define MREPEAT159(macro, data) MREPEAT158(macro, data) macro(158, data) +#define MREPEAT160(macro, data) MREPEAT159(macro, data) macro(159, data) +#define MREPEAT161(macro, data) MREPEAT160(macro, data) macro(160, data) +#define MREPEAT162(macro, data) MREPEAT161(macro, data) macro(161, data) +#define MREPEAT163(macro, data) MREPEAT162(macro, data) macro(162, data) +#define MREPEAT164(macro, data) MREPEAT163(macro, data) macro(163, data) +#define MREPEAT165(macro, data) MREPEAT164(macro, data) macro(164, data) +#define MREPEAT166(macro, data) MREPEAT165(macro, data) macro(165, data) +#define MREPEAT167(macro, data) MREPEAT166(macro, data) macro(166, data) +#define MREPEAT168(macro, data) MREPEAT167(macro, data) macro(167, data) +#define MREPEAT169(macro, data) MREPEAT168(macro, data) macro(168, data) +#define MREPEAT170(macro, data) MREPEAT169(macro, data) macro(169, data) +#define MREPEAT171(macro, data) MREPEAT170(macro, data) macro(170, data) +#define MREPEAT172(macro, data) MREPEAT171(macro, data) macro(171, data) +#define MREPEAT173(macro, data) MREPEAT172(macro, data) macro(172, data) +#define MREPEAT174(macro, data) MREPEAT173(macro, data) macro(173, data) +#define MREPEAT175(macro, data) MREPEAT174(macro, data) macro(174, data) +#define MREPEAT176(macro, data) MREPEAT175(macro, data) macro(175, data) +#define MREPEAT177(macro, data) MREPEAT176(macro, data) macro(176, data) +#define MREPEAT178(macro, data) MREPEAT177(macro, data) macro(177, data) +#define MREPEAT179(macro, data) MREPEAT178(macro, data) macro(178, data) +#define MREPEAT180(macro, data) MREPEAT179(macro, data) macro(179, data) +#define MREPEAT181(macro, data) MREPEAT180(macro, data) macro(180, data) +#define MREPEAT182(macro, data) MREPEAT181(macro, data) macro(181, data) +#define MREPEAT183(macro, data) MREPEAT182(macro, data) macro(182, data) +#define MREPEAT184(macro, data) MREPEAT183(macro, data) macro(183, data) +#define MREPEAT185(macro, data) MREPEAT184(macro, data) macro(184, data) +#define MREPEAT186(macro, data) MREPEAT185(macro, data) macro(185, data) +#define MREPEAT187(macro, data) MREPEAT186(macro, data) macro(186, data) +#define MREPEAT188(macro, data) MREPEAT187(macro, data) macro(187, data) +#define MREPEAT189(macro, data) MREPEAT188(macro, data) macro(188, data) +#define MREPEAT190(macro, data) MREPEAT189(macro, data) macro(189, data) +#define MREPEAT191(macro, data) MREPEAT190(macro, data) macro(190, data) +#define MREPEAT192(macro, data) MREPEAT191(macro, data) macro(191, data) +#define MREPEAT193(macro, data) MREPEAT192(macro, data) macro(192, data) +#define MREPEAT194(macro, data) MREPEAT193(macro, data) macro(193, data) +#define MREPEAT195(macro, data) MREPEAT194(macro, data) macro(194, data) +#define MREPEAT196(macro, data) MREPEAT195(macro, data) macro(195, data) +#define MREPEAT197(macro, data) MREPEAT196(macro, data) macro(196, data) +#define MREPEAT198(macro, data) MREPEAT197(macro, data) macro(197, data) +#define MREPEAT199(macro, data) MREPEAT198(macro, data) macro(198, data) +#define MREPEAT200(macro, data) MREPEAT199(macro, data) macro(199, data) +#define MREPEAT201(macro, data) MREPEAT200(macro, data) macro(200, data) +#define MREPEAT202(macro, data) MREPEAT201(macro, data) macro(201, data) +#define MREPEAT203(macro, data) MREPEAT202(macro, data) macro(202, data) +#define MREPEAT204(macro, data) MREPEAT203(macro, data) macro(203, data) +#define MREPEAT205(macro, data) MREPEAT204(macro, data) macro(204, data) +#define MREPEAT206(macro, data) MREPEAT205(macro, data) macro(205, data) +#define MREPEAT207(macro, data) MREPEAT206(macro, data) macro(206, data) +#define MREPEAT208(macro, data) MREPEAT207(macro, data) macro(207, data) +#define MREPEAT209(macro, data) MREPEAT208(macro, data) macro(208, data) +#define MREPEAT210(macro, data) MREPEAT209(macro, data) macro(209, data) +#define MREPEAT211(macro, data) MREPEAT210(macro, data) macro(210, data) +#define MREPEAT212(macro, data) MREPEAT211(macro, data) macro(211, data) +#define MREPEAT213(macro, data) MREPEAT212(macro, data) macro(212, data) +#define MREPEAT214(macro, data) MREPEAT213(macro, data) macro(213, data) +#define MREPEAT215(macro, data) MREPEAT214(macro, data) macro(214, data) +#define MREPEAT216(macro, data) MREPEAT215(macro, data) macro(215, data) +#define MREPEAT217(macro, data) MREPEAT216(macro, data) macro(216, data) +#define MREPEAT218(macro, data) MREPEAT217(macro, data) macro(217, data) +#define MREPEAT219(macro, data) MREPEAT218(macro, data) macro(218, data) +#define MREPEAT220(macro, data) MREPEAT219(macro, data) macro(219, data) +#define MREPEAT221(macro, data) MREPEAT220(macro, data) macro(220, data) +#define MREPEAT222(macro, data) MREPEAT221(macro, data) macro(221, data) +#define MREPEAT223(macro, data) MREPEAT222(macro, data) macro(222, data) +#define MREPEAT224(macro, data) MREPEAT223(macro, data) macro(223, data) +#define MREPEAT225(macro, data) MREPEAT224(macro, data) macro(224, data) +#define MREPEAT226(macro, data) MREPEAT225(macro, data) macro(225, data) +#define MREPEAT227(macro, data) MREPEAT226(macro, data) macro(226, data) +#define MREPEAT228(macro, data) MREPEAT227(macro, data) macro(227, data) +#define MREPEAT229(macro, data) MREPEAT228(macro, data) macro(228, data) +#define MREPEAT230(macro, data) MREPEAT229(macro, data) macro(229, data) +#define MREPEAT231(macro, data) MREPEAT230(macro, data) macro(230, data) +#define MREPEAT232(macro, data) MREPEAT231(macro, data) macro(231, data) +#define MREPEAT233(macro, data) MREPEAT232(macro, data) macro(232, data) +#define MREPEAT234(macro, data) MREPEAT233(macro, data) macro(233, data) +#define MREPEAT235(macro, data) MREPEAT234(macro, data) macro(234, data) +#define MREPEAT236(macro, data) MREPEAT235(macro, data) macro(235, data) +#define MREPEAT237(macro, data) MREPEAT236(macro, data) macro(236, data) +#define MREPEAT238(macro, data) MREPEAT237(macro, data) macro(237, data) +#define MREPEAT239(macro, data) MREPEAT238(macro, data) macro(238, data) +#define MREPEAT240(macro, data) MREPEAT239(macro, data) macro(239, data) +#define MREPEAT241(macro, data) MREPEAT240(macro, data) macro(240, data) +#define MREPEAT242(macro, data) MREPEAT241(macro, data) macro(241, data) +#define MREPEAT243(macro, data) MREPEAT242(macro, data) macro(242, data) +#define MREPEAT244(macro, data) MREPEAT243(macro, data) macro(243, data) +#define MREPEAT245(macro, data) MREPEAT244(macro, data) macro(244, data) +#define MREPEAT246(macro, data) MREPEAT245(macro, data) macro(245, data) +#define MREPEAT247(macro, data) MREPEAT246(macro, data) macro(246, data) +#define MREPEAT248(macro, data) MREPEAT247(macro, data) macro(247, data) +#define MREPEAT249(macro, data) MREPEAT248(macro, data) macro(248, data) +#define MREPEAT250(macro, data) MREPEAT249(macro, data) macro(249, data) +#define MREPEAT251(macro, data) MREPEAT250(macro, data) macro(250, data) +#define MREPEAT252(macro, data) MREPEAT251(macro, data) macro(251, data) +#define MREPEAT253(macro, data) MREPEAT252(macro, data) macro(252, data) +#define MREPEAT254(macro, data) MREPEAT253(macro, data) macro(253, data) +#define MREPEAT255(macro, data) MREPEAT254(macro, data) macro(254, data) +#define MREPEAT256(macro, data) MREPEAT255(macro, data) macro(255, data) + +/** + * \} + */ + +#endif // _MREPEAT_H_ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/preprocessor.h b/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/preprocessor.h new file mode 100644 index 00000000..b92a112c --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/preprocessor.h @@ -0,0 +1,51 @@ +/** + * \file + * + * \brief Preprocessor utils. + * + * Copyright (c) 2009 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _PREPROCESSOR_H_ +#define _PREPROCESSOR_H_ + +#include "tpaste.h" +#include "stringz.h" +#include "mrepeat.h" + + +#endif // _PREPROCESSOR_H_ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/stringz.h b/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/stringz.h new file mode 100644 index 00000000..69c8eace --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/stringz.h @@ -0,0 +1,81 @@ +/** + * \file + * + * \brief Preprocessor stringizing utils. + * + * Copyright (c) 2009 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _STRINGZ_H_ +#define _STRINGZ_H_ + +/** + * \defgroup group_xmega_utils_stringz Stringize + * + * \ingroup group_xmega_utils + * + * \{ + */ + +/*! \brief Stringize. + * + * Stringize a preprocessing token, this token being allowed to be \#defined. + * + * May be used only within macros with the token passed as an argument if the token is \#defined. + * + * For example, writing STRINGZ(PIN) within a macro \#defined by PIN_NAME(PIN) + * and invoked as PIN_NAME(PIN0) with PIN0 \#defined as A0 is equivalent to + * writing "A0". + */ +#define STRINGZ(x) #x + +/*! \brief Absolute stringize. + * + * Stringize a preprocessing token, this token being allowed to be \#defined. + * + * No restriction of use if the token is \#defined. + * + * For example, writing ASTRINGZ(PIN0) anywhere with PIN0 \#defined as A0 is + * equivalent to writing "A0". + */ +#define ASTRINGZ(x) STRINGZ(x) + +/** + * \} + */ + +#endif // _STRINGZ_H_ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/tpaste.h b/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/tpaste.h new file mode 100644 index 00000000..a948ff66 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/utils/preprocessor/tpaste.h @@ -0,0 +1,101 @@ +/** + * \file + * + * \brief Preprocessor token pasting utils. + * + * Copyright (c) 2009 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _TPASTE_H_ +#define _TPASTE_H_ + +/** + * \defgroup group_xmega_utils_tpaste Token Paste + * + * \ingroup group_xmega_utils + * + * \{ + */ + +/*! \name Token Paste + * + * Paste N preprocessing tokens together, these tokens being allowed to be \#defined. + * + * May be used only within macros with the tokens passed as arguments if the tokens are \#defined. + * + * For example, writing TPASTE2(U, WIDTH) within a macro \#defined by + * UTYPE(WIDTH) and invoked as UTYPE(UL_WIDTH) with UL_WIDTH \#defined as 32 is + * equivalent to writing U32. + */ +//! @{ +#define TPASTE2( a, b) a##b +#define TPASTE3( a, b, c) a##b##c +#define TPASTE4( a, b, c, d) a##b##c##d +#define TPASTE5( a, b, c, d, e) a##b##c##d##e +#define TPASTE6( a, b, c, d, e, f) a##b##c##d##e##f +#define TPASTE7( a, b, c, d, e, f, g) a##b##c##d##e##f##g +#define TPASTE8( a, b, c, d, e, f, g, h) a##b##c##d##e##f##g##h +#define TPASTE9( a, b, c, d, e, f, g, h, i) a##b##c##d##e##f##g##h##i +#define TPASTE10(a, b, c, d, e, f, g, h, i, j) a##b##c##d##e##f##g##h##i##j +//! @} + +/*! \name Absolute Token Paste + * + * Paste N preprocessing tokens together, these tokens being allowed to be \#defined. + * + * No restriction of use if the tokens are \#defined. + * + * For example, writing ATPASTE2(U, UL_WIDTH) anywhere with UL_WIDTH \#defined + * as 32 is equivalent to writing U32. + */ +//! @{ +#define ATPASTE2( a, b) TPASTE2( a, b) +#define ATPASTE3( a, b, c) TPASTE3( a, b, c) +#define ATPASTE4( a, b, c, d) TPASTE4( a, b, c, d) +#define ATPASTE5( a, b, c, d, e) TPASTE5( a, b, c, d, e) +#define ATPASTE6( a, b, c, d, e, f) TPASTE6( a, b, c, d, e, f) +#define ATPASTE7( a, b, c, d, e, f, g) TPASTE7( a, b, c, d, e, f, g) +#define ATPASTE8( a, b, c, d, e, f, g, h) TPASTE8( a, b, c, d, e, f, g, h) +#define ATPASTE9( a, b, c, d, e, f, g, h, i) TPASTE9( a, b, c, d, e, f, g, h, i) +#define ATPASTE10(a, b, c, d, e, f, g, h, i, j) TPASTE10(a, b, c, d, e, f, g, h, i, j) +//! @} + +/** + * \} + */ + +#endif // _TPASTE_H_ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/utils/progmem.h b/bacnet-stack/ports/xplained/ASF/xmega/utils/progmem.h new file mode 100644 index 00000000..dfa35140 --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/utils/progmem.h @@ -0,0 +1,99 @@ +/** + * \file + * + * \brief Program memory access + * + * Copyright (c) 2010 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#ifndef UTILS_PROGMEM_H +#define UTILS_PROGMEM_H + +/** + * \defgroup group_xmega_utils_progmem Program memory + * + * \ingroup group_xmega_utils + * + * \{ + */ + +/*! \name Program memory + * + * Macros for locating and accessing data in program memory. + * + * @{ + */ +#if defined(__GNUC__) || defined(__DOXYGEN__) +# include +# define PROGMEM_LOCATION(type, name, loc) \ + type name __attribute__((section (#loc))) +# define PROGMEM_DECLARE(type, name) const type name __attribute__((__progmem__)) +# define PROGMEM_STRING(x) PSTR(x) +# define PROGMEM_STRING_T PGM_P +# define PROGMEM_T const +# define PROGMEM_PTR_T const * +# define PROGMEM_BYTE_ARRAY_T uint8_t* +# define PROGMEM_WORD_ARRAY_T uint16_t* +# define PROGMEM_READ_BYTE(x) pgm_read_byte(x) +# define PROGMEM_READ_WORD(x) pgm_read_word(x) + +#elif defined(__ICCAVR__) +# include +# ifndef __HAS_ELPM__ +# define _MEMATTR_ASF __flash +# else /* __HAS_ELPM__ */ +# define _MEMATTR_ASF __hugeflash +# endif /* __HAS_ELPM__ */ +# define PROGMEM_LOCATION(type, name, loc) const _MEMATTR_ASF type name @ loc +# define PROGMEM_DECLARE(type, name) _MEMATTR_ASF type name +# define PROGMEM_STRING(x) ((_MEMATTR_ASF const char *)(x)) +# define PROGMEM_STRING_T char const _MEMATTR_ASF * +# define PROGMEM_T const _MEMATTR_ASF +# define PROGMEM_PTR_T const _MEMATTR_ASF * +# define PROGMEM_BYTE_ARRAY_T uint8_t const _MEMATTR_ASF * +# define PROGMEM_WORD_ARRAY_T uint16_t const _MEMATTR_ASF * +# define PROGMEM_READ_BYTE(x) *(x) +# define PROGMEM_READ_WORD(x) *(x) +#endif +//! @} + +/** + * \} + */ + +#endif /* UTILS_PROGMEM_H */ diff --git a/bacnet-stack/ports/xplained/ASF/xmega/utils/status_codes.h b/bacnet-stack/ports/xplained/ASF/xmega/utils/status_codes.h new file mode 100644 index 00000000..8fe15e9a --- /dev/null +++ b/bacnet-stack/ports/xplained/ASF/xmega/utils/status_codes.h @@ -0,0 +1,119 @@ +/** + * \file + * + * \brief Status code definitions. + * + * This file defines various status codes returned by functions, + * indicating success or failure as well as what kind of failure. + * + * Copyright (c) 2009-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef STATUS_CODES_H_INCLUDED +#define STATUS_CODES_H_INCLUDED + +/** + * \defgroup group_xmega_utils_status_codes Status Codes + * + * \ingroup group_xmega_utils + * + * \{ + */ + +/* Note: this is a local workaround to avoid a pre-processor clash due to the + * lwIP macro ERR_TIMEOUT. */ +#if defined(__LWIP_ERR_H__) && defined(ERR_TIMEOUT) +#if (ERR_TIMEOUT != -3) + +/* Internal check to make sure that the later restore of lwIP's ERR_TIMEOUT + * macro is set to the correct value. Note that it is highly improbable that + * this value ever changes in lwIP. */ +#error ASF developers: check lwip err.h new value for ERR_TIMEOUT +#endif +#undef ERR_TIMEOUT +#endif + +/** + * Status code that may be returned by shell commands and protocol + * implementations. + * + * \note Any change to these status codes and the corresponding + * message strings is strictly forbidden. New codes can be added, + * however, but make sure that any message string tables are updated + * at the same time. + */ +enum status_code +{ + STATUS_OK = 0, //!< Success + ERR_IO_ERROR = -1, //!< I/O error + ERR_FLUSHED = -2, //!< Request flushed from queue + ERR_TIMEOUT = -3, //!< Operation timed out + ERR_BAD_DATA = -4, //!< Data integrity check failed + ERR_PROTOCOL = -5, //!< Protocol error + ERR_UNSUPPORTED_DEV = -6, //!< Unsupported device + ERR_NO_MEMORY = -7, //!< Insufficient memory + ERR_INVALID_ARG = -8, //!< Invalid argument + ERR_BAD_ADDRESS = -9, //!< Bad address + ERR_BUSY = -10, //!< Resource is busy + ERR_BAD_FORMAT = -11, //!< Data format not recognized + ERR_NO_TIMER = -12, //!< No timer available + ERR_TIMER_ALREADY_RUNNING = -13, //!< Timer already running + ERR_TIMER_NOT_RUNNING = -14, //!< Timer not running + + /** + * \brief Operation in progress + * + * This status code is for driver-internal use when an operation + * is currently being performed. + * + * \note Drivers should never return this status code to any + * callers. It is strictly for internal use. + */ + OPERATION_IN_PROGRESS = -128, +}; + +typedef enum status_code status_code_t; + +#if defined(__LWIP_ERR_H__) +#define ERR_TIMEOUT -3 +#endif + +/** + * \} + */ + +#endif /* STATUS_CODES_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/adc-hdw.c b/bacnet-stack/ports/xplained/adc-hdw.c new file mode 100644 index 00000000..3340bec9 --- /dev/null +++ b/bacnet-stack/ports/xplained/adc-hdw.c @@ -0,0 +1,183 @@ +/************************************************************************** +* +* Copyright (C) 2014 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "adc-hdw.h" + +/* samples */ +#define ADC_CHANNELS_MAX 10 +static uint16_t ADC_Channel_Value[ADC_CHANNELS_MAX]; +static uint8_t ADC_Current_Channel; + +/************************************************************************* +* DESCRIPTION: set the active channel in the ADC +* RETURN: nothing +* NOTES: called from ISR, so handle as non-blocking +**************************************************************************/ +static void adc_set_channel(unsigned channel) +{ + struct adc_channel_config adc_ch_conf; + + ADC_Current_Channel = channel; + adcch_read_configuration(&ADCA, ADC_CH0, &adc_ch_conf); + switch (channel) { + case 0: + adcch_set_input(&adc_ch_conf, ADCCH_POS_PIN7, ADCCH_NEG_PIN1, 1); + break; + case 1: + adcch_set_input(&adc_ch_conf, ADCCH_POS_PIN8, ADCCH_NEG_PIN1, 1); + break; + case 2: + adcch_set_input(&adc_ch_conf, ADCCH_POS_PIN9, ADCCH_NEG_PIN1, 1); + break; + case 3: + adcch_set_input(&adc_ch_conf, ADCCH_POS_PIN10, ADCCH_NEG_PIN1, 1); + break; + case 4: + adcch_set_input(&adc_ch_conf, ADCCH_POS_PIN11, ADCCH_NEG_PIN1, 1); + break; + case 5: + adcch_set_input(&adc_ch_conf, ADCCH_POS_PIN2, ADCCH_NEG_PIN1, 1); + break; + case 6: + adcch_set_input(&adc_ch_conf, ADCCH_POS_PIN3, ADCCH_NEG_PIN1, 1); + break; + case 7: + adcch_set_input(&adc_ch_conf, ADCCH_POS_PIN4, ADCCH_NEG_PIN1, 1); + break; + case 8: + adcch_set_input(&adc_ch_conf, ADCCH_POS_PIN5, ADCCH_NEG_PIN1, 1); + break; + case 9: + adcch_set_input(&adc_ch_conf, ADCCH_POS_PIN6, ADCCH_NEG_PIN1, 1); + break; + default: + break; + } + adcch_set_interrupt_mode(&adc_ch_conf, ADCCH_MODE_COMPLETE); + adcch_enable_interrupt(&adc_ch_conf); + adcch_write_configuration(&ADCA, ADC_CH0, &adc_ch_conf); +} + +/************************************************************************* +* DESCRIPTION: run the active channels through the ADC +* RETURN: nothing +* NOTES: called from ISR, so handle as non-blocking +**************************************************************************/ +static void adc_handler(ADC_t *adc, uint8_t ch_mask, adc_result_t raw_value) +{ + unsigned channel; + + channel = ADC_Current_Channel; + if (channel < ADC_CHANNELS_MAX) { + ADC_Channel_Value[channel] = raw_value; + } + channel++; + if (channel >= ADC_CHANNELS_MAX) { + channel = 0; + } + adc_set_channel(channel); +} + +/************************************************************************* +* DESCRIPTION: initialize Analog to Digital Converter (ADC) +* RETURN: nothing +* NOTES: none +**************************************************************************/ +void adc_init(void) +{ + struct adc_config adc_conf; + + ioport_configure_pin(IOPORT_CREATE_PIN(PORTA, 7),IOPORT_DIR_INPUT); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTB, 0),IOPORT_DIR_INPUT); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTB, 1),IOPORT_DIR_INPUT); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTB, 2),IOPORT_DIR_INPUT); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTB, 3),IOPORT_DIR_INPUT); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTA, 2),IOPORT_DIR_INPUT); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTA, 3),IOPORT_DIR_INPUT); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTA, 4),IOPORT_DIR_INPUT); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTA, 5),IOPORT_DIR_INPUT); + ioport_configure_pin(IOPORT_CREATE_PIN(PORTA, 6),IOPORT_DIR_INPUT); + /* Clear the ADC configuration structs */ + adc_read_configuration(&ADCA, &adc_conf); + adc_set_conversion_parameters(&adc_conf, ADC_SIGN_ON, ADC_RES_12, ADC_REF_AREFA); + adc_set_clock_rate(&adc_conf, 200000UL); + adc_set_conversion_trigger(&adc_conf, ADC_TRIG_MANUAL, 1, 0); + adc_write_configuration(&ADCA, &adc_conf); + adc_set_callback(&ADCA, &adc_handler); + adc_set_channel(0); + /* Enable the ADC and start the first conversion. */ + adc_enable(&ADCA); +} + +/************************************************************************* +* DESCRIPTION: Get a result from the ADC 10-bit value +* RETURN: 12-bit ADC value +* NOTES: channel 0..9 are supported +**************************************************************************/ +uint16_t adc_result_12bit(uint8_t channel) +{ + uint16_t value = 0; + + if (channel < ADC_CHANNELS_MAX) { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + value = ADC_Channel_Value[channel]; + } + } + + return value; +} + +/************************************************************************* +* DESCRIPTION: Get a result from the ADC 10-bit value +* RETURN: 10-bit ADC value +* NOTES: channel 0..9 are supported +**************************************************************************/ +uint16_t adc_result_10bit(uint8_t channel) +{ + uint16_t result; + + result = adc_result_12bit(channel); + result >>= 2; + + return result; +} + +/************************************************************************* +* DESCRIPTION: Get a result from the ADC 8-bit value +* RETURN: 8-bit ADC value +* NOTES: channel 0..9 are supported +**************************************************************************/ +uint8_t adc_result_8bit(uint8_t channel) +{ + uint16_t result; + + result = adc_result_12bit(channel); + result >>= 4; + + return result; +} diff --git a/bacnet-stack/ports/xplained/adc-hdw.h b/bacnet-stack/ports/xplained/adc-hdw.h new file mode 100644 index 00000000..36a5baa1 --- /dev/null +++ b/bacnet-stack/ports/xplained/adc-hdw.h @@ -0,0 +1,42 @@ +/************************************************************************** +* +* Copyright (C) 2014 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef ADC_HDW_H +#define ADC_HDW_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + void adc_init (void); + uint16_t adc_result_12bit(uint8_t channel); + uint16_t adc_result_10bit(uint8_t channel); + uint8_t adc_result_8bit(uint8_t channel); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/xplained/ai.c b/bacnet-stack/ports/xplained/ai.c new file mode 100644 index 00000000..725bf41d --- /dev/null +++ b/bacnet-stack/ports/xplained/ai.c @@ -0,0 +1,373 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" +#include "ai.h" +#include "handlers.h" + +#ifndef MAX_ANALOG_INPUTS +#define MAX_ANALOG_INPUTS 2 +#endif + +static float Present_Value[MAX_ANALOG_INPUTS]; +static bool Out_Of_Service[MAX_ANALOG_INPUTS]; +static BACNET_ENGINEERING_UNITS Units[MAX_ANALOG_INPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Analog_Input_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_UNITS, + -1 +}; + +static const int Analog_Input_Properties_Optional[] = { + -1 +}; + +static const int Analog_Input_Properties_Proprietary[] = { + -1 +}; + +void Analog_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Analog_Input_Properties_Required; + if (pOptional) + *pOptional = Analog_Input_Properties_Optional; + if (pProprietary) + *pProprietary = Analog_Input_Properties_Proprietary; + + return; +} + +void Analog_Input_Init( + void) +{ + return; +} + +/* we simply have 0-n object instances. */ +uint32_t Analog_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Analog_Input_Instance_To_Index( + uint32_t instance) +{ + return instance; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Input_Valid_Instance( + uint32_t object_instance) +{ + unsigned index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + return true; + } + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Analog_Input_Count( + void) +{ + return MAX_ANALOG_INPUTS; +} + +bool Analog_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32]; /* okay for single thread */ + bool status = false; + unsigned index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + sprintf(text_string, "AI-%lu", object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +float Analog_Input_Present_Value( + uint32_t object_instance) +{ + float value = 0.0; + unsigned index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + value = Present_Value[index]; + } + + return value; +} + +void Analog_Input_Present_Value_Set( + uint32_t object_instance, + float value) +{ + unsigned index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + Present_Value[index] = value; + } +} + +bool Analog_Input_Out_Of_Service( + uint32_t object_instance) +{ + unsigned index = 0; + bool value = false; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + value = Out_Of_Service[index]; + } + + return value; +} + +void Analog_Input_Out_Of_Service_Set( + uint32_t object_instance, + bool value) +{ + unsigned index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + Out_Of_Service[index] = value; + } +} + +bool Analog_Input_Units_Set( + uint32_t object_instance, + uint16_t value) +{ + unsigned index = 0; + bool status = false; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + Units[index] = value; + status = true; + } + + return status; +} + +uint16_t Analog_Input_Units( + uint32_t object_instance) +{ + unsigned index = 0; + uint16_t value = UNITS_NO_UNITS; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + value = Units[index]; + } + + return value; +} + +/* return apdu length, or -1 on error */ +/* assumption - object already exists */ +int Analog_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_CHARACTER_STRING char_string = { 0 }; + BACNET_BIT_STRING bit_string = { 0 }; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], rpdata->object_type, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Analog_Input_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], rpdata->object_type); + break; + case PROP_PRESENT_VALUE: + apdu_len = + encode_application_real(&apdu[0], + Analog_Input_Present_Value(rpdata->object_instance)); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + Analog_Input_Out_Of_Service(rpdata->object_instance)); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], + Analog_Input_Out_Of_Service(rpdata->object_instance)); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], + Analog_Input_Units(rpdata->object_instance)); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Input_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_EVENT_TIME_STAMPS) && + (wp_data->object_property != PROP_PROPERTY_LIST) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch ((int) wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (Analog_Input_Out_Of_Service(wp_data->object_instance)) { + Analog_Input_Present_Value_Set(wp_data->object_instance, + value.type.Real); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + status = false; + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Analog_Input_Out_Of_Service_Set( + wp_data->object_instance, + value.type.Boolean); + } + break; + case PROP_UNITS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Analog_Input_Out_Of_Service_Set( + wp_data->object_instance, + value.type.Enumerated); + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} diff --git a/bacnet-stack/ports/xplained/asf.h b/bacnet-stack/ports/xplained/asf.h new file mode 100644 index 00000000..b5c59dcb --- /dev/null +++ b/bacnet-stack/ports/xplained/asf.h @@ -0,0 +1,134 @@ +/** + * \file + * + * \brief Autogenerated API include file for the Atmel Software Framework (ASF) + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#ifndef ASF_H +#define ASF_H + +/* + * This file includes all API header files for the selected drivers from ASF. + * Note: There might be duplicate includes required by more than one driver. + * + * The file is automatically generated and will be re-written when + * running the ASF driver selector tool. Any changes will be discarded. + */ + +// From module: ADC - XMEGA A/AU Implementation +#include + +// From module: CPU specific features +#include +#include + +// From module: Delay routines +#include + +// From module: GPIO - General purpose Input/Output +#include + +// From module: Generic board support +#include + +// From module: IOPORT - General purpose I/O service +#include + +// From module: Interrupt management - XMEGA implementation +#include + +// From module: NVM - Non Volatile Memory +#include + +// From module: NVM - Non volatile memory access +#include + +// From module: PMIC - Programmable Multi-level Interrupt Controller +#include + +// From module: PWM service using timer/counter +#include + +// From module: Part identification macros +#include + +// From module: RTC32 - Real Time Counter 32 +#include + +// From module: Sleep Controller driver +#include + +// From module: Sleep manager - XMEGA A/AU/B/D implementation +#include +#include + +// From module: Standard serial I/O (stdio) - XMEGA implementation +#include + +// From module: System Clock Control - XMEGA A1U/A3U/A3BU/A4U/B/C implementation +#include + +// From module: TC - Timer Counter +#include + +// From module: Timeout Service XMEGA +#include + +// From module: USART - Serial interface - XMEGA implementation +#include + +// From module: TWI - Two-wire Master and Slave Interface +//#include +//#include + +// From module: USART - Universal Synchronous/Asynchronous Receiver/Transmitter +#include + +// From module: WDT - Watchdog Timer +#include + +// From module: XMEGA compiler driver +#include +#include + +// From module: XMEGA-A3BU Xplained LED support enabled +#include + +#endif // ASF_H diff --git a/bacnet-stack/ports/xplained/bacnet.atsln b/bacnet-stack/ports/xplained/bacnet.atsln new file mode 100644 index 00000000..c97eac54 --- /dev/null +++ b/bacnet-stack/ports/xplained/bacnet.atsln @@ -0,0 +1,23 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Atmel Studio Solution File, Format Version 11.00 +Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "bacnet", "bacnet.cproj", "{EA031B72-CE11-41CC-BFDC-00625D02A537}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|AVR = Debug|AVR + Debug-XPLAINED|AVR = Debug-XPLAINED|AVR + Release|AVR = Release|AVR + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EA031B72-CE11-41CC-BFDC-00625D02A537}.Debug|AVR.ActiveCfg = Debug|AVR + {EA031B72-CE11-41CC-BFDC-00625D02A537}.Debug|AVR.Build.0 = Debug|AVR + {EA031B72-CE11-41CC-BFDC-00625D02A537}.Debug-XPLAINED|AVR.ActiveCfg = Debug-XPLAINED|AVR + {EA031B72-CE11-41CC-BFDC-00625D02A537}.Debug-XPLAINED|AVR.Build.0 = Debug-XPLAINED|AVR + {EA031B72-CE11-41CC-BFDC-00625D02A537}.Release|AVR.ActiveCfg = Release|AVR + {EA031B72-CE11-41CC-BFDC-00625D02A537}.Release|AVR.Build.0 = Release|AVR + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/bacnet-stack/ports/xplained/bacnet.c b/bacnet-stack/ports/xplained/bacnet.c new file mode 100644 index 00000000..4f4345ea --- /dev/null +++ b/bacnet-stack/ports/xplained/bacnet.c @@ -0,0 +1,233 @@ +/************************************************************************** +* +* Copyright (C) 2013 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +/* hardware layer includes */ +#include "rs485.h" +#include "nvmdata.h" +#include "wdt.h" +#include "led.h" +#include "adc-hdw.h" +/* BACnet Stack includes */ +#include "datalink.h" +#include "npdu.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dcc.h" +#include "iam.h" +#include "timer.h" +#include "tsm.h" +#include "ringbuf.h" +/* BACnet objects */ +#include "device.h" +#include "ai.h" +/* me */ +#include "bacnet.h" + +/* buffer for incoming BACnet messages */ +struct mstp_rx_packet { + BACNET_ADDRESS src; + uint16_t length; + uint8_t buffer[MAX_MPDU]; +}; +/* count must be a power of 2 for ringbuf library */ +#ifndef MSTP_RECEIVE_PACKET_COUNT +#define MSTP_RECEIVE_PACKET_COUNT 2 +#endif +static volatile struct mstp_rx_packet Receive_Buffer[MSTP_RECEIVE_PACKET_COUNT]; +static RING_BUFFER Receive_Queue; +/* Device ID to track changes */ +static uint32_t Device_ID = 0xFFFFFFFF; +/* timer for device communications control */ +static struct itimer DCC_Timer; +#define DCC_CYCLE_SECONDS 1 +/* timer for COV */ +static struct itimer COV_Timer; +#define COV_CYCLE_SECONDS 1 +/* timer for TSM */ +static struct itimer TSM_Timer; +#define TSM_CYCLE_SECONDS 1 +/* timer for Reinit */ +static struct itimer Reinit_Timer; +/* buffer for incoming packets */ +static uint8_t PDUBuffer[MAX_MPDU]; + +/************************************************************************** +* Description: handles reinitializing the device after a few seconds +* Returns: none +* Notes: gives the device enough time to acknowledge the RD request +**************************************************************************/ +static void reinit_task(void) +{ + BACNET_REINITIALIZED_STATE state = BACNET_REINIT_IDLE; + + state = Device_Reinitialized_State(); + if (state == BACNET_REINIT_IDLE) { + /* set timer to never expire */ + timer_interval_infinity(&Reinit_Timer); + } else if (timer_interval_active(&Reinit_Timer)) { + if (timer_interval_expired(&Reinit_Timer)) { + /* reset MCU via watchdog timeout */ + wdt_reset_mcu(); + } + } else { + timer_interval_start_seconds(&Reinit_Timer, 3); + } +} + +/************************************************************************** +* Description: handles recurring strictly timed task +* Returns: none +* Notes: called by ISR every 5 milliseconds +**************************************************************************/ +void bacnet_task_timed( + void) +{ + struct mstp_rx_packet *pkt = NULL; + uint16_t pdu_len = 0; + BACNET_ADDRESS src; + + pdu_len = dlmstp_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 5); + if (pdu_len) { + pkt = (struct mstp_rx_packet *) Ringbuf_Alloc(&Receive_Queue); + if (pkt) { + memcpy(pkt->buffer, PDUBuffer, MAX_MPDU); + bacnet_address_copy(&pkt->src, &src); + pkt->length = pdu_len; + } + } +} + +/************************************************************************** +* Description: handles recurring task +* Returns: none +* Notes: none +**************************************************************************/ +static void bacnet_test_task(void) +{ + static unsigned index = 0; + uint32_t instance; + float float_value; + uint16_t adc_value; + + instance = Analog_Input_Index_To_Instance(index); + if (!Analog_Input_Out_Of_Service(instance)) { + adc_value = adc_result_12bit(index); + float_value = adc_value; + float_value /= 4095; + Analog_Input_Present_Value_Set(instance, float_value); + } + index++; + if (index >= MAX_ANALOG_INPUTS) { + index = 0; + } +} + +/************************************************************************** +* Description: handles recurring task +* Returns: none +* Notes: none +**************************************************************************/ +void bacnet_task(void) +{ + struct mstp_rx_packet pkt = {{0}}; + bool pdu_available = false; + + /* hello, World! */ + if (Device_ID != Device_Object_Instance_Number()) { + Device_ID = Device_Object_Instance_Number(); + Send_I_Am(&Handler_Transmit_Buffer[0]); + } + /* handle the timers */ + if (timer_interval_expired(&DCC_Timer)) { + timer_interval_reset(&DCC_Timer); + dcc_timer_seconds(DCC_CYCLE_SECONDS); + led_on_interval(LED_DEBUG,500); + } + if (timer_interval_expired(&TSM_Timer)) { + timer_interval_reset(&TSM_Timer); + tsm_timer_milliseconds(timer_interval(&TSM_Timer)); + } + reinit_task(); + bacnet_test_task(); + /* handle the messaging */ + if ((!dlmstp_send_pdu_queue_full()) && + (!Ringbuf_Empty(&Receive_Queue))) { + Ringbuf_Pop(&Receive_Queue, (uint8_t *)&pkt); + pdu_available = true; + } + if (pdu_available) { + led_on_interval(LED_APDU,125); + npdu_handler(&pkt.src, &pkt.buffer[0], pkt.length); + } +} + +/************************************************************************** +* Description: initializes the BACnet library +* Returns: none +* Notes: none +**************************************************************************/ +void bacnet_init(void) +{ + unsigned i; + + Ringbuf_Init(&Receive_Queue, (uint8_t *) & Receive_Buffer, + sizeof(struct mstp_rx_packet), MSTP_RECEIVE_PACKET_COUNT); + dlmstp_init(NULL); + /* initialize objects */ + Device_Init(NULL); + /* set up our confirmed service unrecognized service handler - required! */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + /* handle communication so we can shut up when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); + /* start the cyclic 1 second timer for DCC */ + timer_interval_start_seconds(&DCC_Timer, DCC_CYCLE_SECONDS); + /* start the cyclic 1 second timer for COV */ + timer_interval_start_seconds(&COV_Timer, COV_CYCLE_SECONDS); + /* start the cyclic 1 second timer for TSM */ + timer_interval_start_seconds(&TSM_Timer, TSM_CYCLE_SECONDS); + for (i = 0; i < MAX_ANALOG_INPUTS; i++) { + Analog_Input_Units_Set( + Analog_Input_Index_To_Instance(i), + UNITS_PERCENT); + } +} diff --git a/bacnet-stack/ports/xplained/bacnet.cproj b/bacnet-stack/ports/xplained/bacnet.cproj new file mode 100644 index 00000000..9049248d --- /dev/null +++ b/bacnet-stack/ports/xplained/bacnet.cproj @@ -0,0 +1,825 @@ + + + + 2.0 + 6.2 + com.Atmel.AVRGCC8.C + {ea031b72-ce11-41cc-bfdc-00625d02a537} + ATxmega256A3BU + xmegaau + Executable + C + $(MSBuildProjectName) + .elf + $(MSBuildProjectDirectory)\$(Configuration) + XMEGA_A3BU_XPLAINED_DEMO1 + BACnet XPLAINED + XMEGA_A3BU_XPLAINED_DEMO1 + Native + true + false + exception_table + 0 + true + 0x20000000 + true + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.atmel.avrdbg.tool.jtagice3plus + JTAG + + com.atmel.avrdbg.tool.jtagicemk3 + JTAGICE3 + J30200019449 + + + + 127.0.0.1 + 1572 + False + + + JTAG + + 0 + 249000 + 7500000 + 125000 + false + false + 0 + 0 + 0 + 0 + 4000000 + 7500000 + + + + true + + + + 7500000 + + JTAG + + com.atmel.avrdbg.tool.jtagice3plus + J30200019449 + JTAGICE3 + + + + + + True + True + True + True + True + False + + IOPORT_XMEGA_COMPAT + BACDL_MSTP + MAX_APDU=128 + MAX_TSM_TRANSACTIONS=1 + MSTP_PDU_PACKET_COUNT=2 + BACNET_VENDOR_ID=293 + MAX_ADDRESS_CACHE=32 + MAX_ANALOG_INPUTS=8 + BOARD=XMEGA_A3BU_XPLAINED + NDEBUG + + ..../config../ASF/xmega/drivers/rtc32../ASF/xmega/drivers/pmic../ASF/xmega/boards/xmega_a3bu_xplained../ASF/xmega/utils/preprocessor../ASF/common/utils../ASF/common/services/sleepmgr../ASF/xmega/drivers/sleep../ASF/common/services/gpio../ASF/xmega/drivers/tc../ASF/xmega/drivers/adc../ASF/xmega/drivers/cpu../ASF/common/boards../ASF/common/services/ioport../ASF/xmega/drivers/nvm../ASF/xmega/boards../ASF/xmega/utils../ASF/xmega/drivers/wdt../ASF/common/services/clock../ASF/common/services/delay../ASF/xmega/drivers/usart../ASF/xmega/services/pwm../ASF/common/drivers/nvm../ASF/common/services/serial/xmega_usart../ASF/common/services/serial../ASF/common/utils/stdio/stdio_serial../ASF/xmega/services/timeout../../../include../../../demo/object + -fdata-sections + True + True + True + -std=gnu99 -fno-strict-aliasing -Wstrict-prototypes -Wmissing-prototypes -Werror-implicit-function-declaration -Wpointer-arith -mrelax + libm + -Wl,--relax -Wl,--section-start=.BOOT=0x40000 + -mrelax -DBOARD=XMEGA_A3BU_XPLAINED -DIOPORT_XMEGA_COMPAT + ../ASF/xmega/drivers/rtc32../ASF/xmega/drivers/pmic../ASF/xmega/boards/xmega_a3bu_xplained../ASF/xmega/drivers/nvm../ASF/xmega/utils/preprocessor../ASF/common/utils../ASF/common/services/sleepmgr../ASF/xmega/drivers/sleep../ASF/common/services/gpio../ASF/xmega/drivers/tc../ASF/xmega/drivers/adc../ASF/xmega/drivers/cpu../ASF/common/boards../ASF/common/services/ioport../ASF/xmega/boards../ASF/xmega/utils../ASF/common/services/clock../ASF/common/services/delay../ASF/xmega/drivers/wdt../ASF/xmega/drivers/usart../config.../ASF/xmega/services/pwm../ASF/common/drivers/nvm../ASF/common/services/serial/xmega_usart../ASF/common/services/serial../ASF/common/utils/stdio/stdio_serial../ASF/xmega/services/timeout + Optimize for size (-Os) + + + + + + + True + True + True + True + True + False + + IOPORT_XMEGA_COMPAT + BACDL_MSTP + MAX_APDU=128 + MAX_TSM_TRANSACTIONS=1 + MSTP_PDU_PACKET_COUNT=2 + BACNET_VENDOR_ID=293 + MAX_ADDRESS_CACHE=32 + MAX_ANALOG_INPUTS=8 + DEBUG + + ..../config../ASF/xmega/drivers/rtc32../ASF/xmega/drivers/pmic../ASF/xmega/boards/xmega_a3bu_xplained../ASF/xmega/utils/preprocessor../ASF/common/utils../ASF/common/services/sleepmgr../ASF/xmega/drivers/sleep../ASF/common/services/gpio../ASF/xmega/drivers/tc../ASF/xmega/drivers/adc../ASF/xmega/drivers/cpu../ASF/common/boards../ASF/common/services/ioport../ASF/xmega/drivers/nvm../ASF/xmega/boards../ASF/xmega/utils../ASF/xmega/drivers/wdt../ASF/common/services/clock../ASF/common/services/delay../ASF/xmega/drivers/usart../ASF/xmega/services/pwm../ASF/common/drivers/nvm../ASF/common/services/serial/xmega_usart../ASF/common/services/serial../ASF/common/utils/stdio/stdio_serial../ASF/xmega/services/timeout../../../include../../../demo/object + -fdata-sections + True + True + True + -std=gnu99 -fno-strict-aliasing -Wstrict-prototypes -Wmissing-prototypes -Werror-implicit-function-declaration -Wpointer-arith -mrelax + libm + -Wl,--relax -Wl,--section-start=.BOOT=0x40000 + -mrelax -DBOARD=XMEGA_A3BU_XPLAINED -DIOPORT_XMEGA_COMPAT + ../ASF/xmega/drivers/rtc32../ASF/xmega/drivers/pmic../ASF/xmega/boards/xmega_a3bu_xplained../ASF/xmega/drivers/nvm../ASF/xmega/utils/preprocessor../ASF/common/utils../ASF/common/services/sleepmgr../ASF/xmega/drivers/sleep../ASF/common/services/gpio../ASF/xmega/drivers/tc../ASF/xmega/drivers/adc../ASF/xmega/drivers/cpu../ASF/common/boards../ASF/common/services/ioport../ASF/xmega/boards../ASF/xmega/utils../ASF/common/services/clock../ASF/common/services/delay../ASF/xmega/drivers/wdt../ASF/xmega/drivers/usart../config.../ASF/xmega/services/pwm../ASF/common/drivers/nvm../ASF/common/services/serial/xmega_usart../ASF/common/services/serial../ASF/common/utils/stdio/stdio_serial../ASF/xmega/services/timeout + Maximum (-g3) + Default (-Wa,-g) + + + + + bin\Debug-XPLAINED\ + + + False + True + True + True + True + False + + IOPORT_XMEGA_COMPAT + BACDL_MSTP + MAX_APDU=128 + MAX_TSM_TRANSACTIONS=1 + MSTP_PDU_PACKET_COUNT=2 + BACNET_VENDOR_ID=293 + MAX_ADDRESS_CACHE=32 + MAX_ANALOG_INPUTS=8 + BOARD=XMEGA_A3BU_XPLAINED + CONF_BOARD_ENABLE_RS485_XPLAINED + DEBUG + + ..../config../ASF/xmega/drivers/rtc32../ASF/xmega/drivers/pmic../ASF/xmega/boards/xmega_a3bu_xplained../ASF/xmega/utils/preprocessor../ASF/common/utils../ASF/common/services/sleepmgr../ASF/xmega/drivers/sleep../ASF/common/services/gpio../ASF/xmega/drivers/tc../ASF/xmega/drivers/adc../ASF/xmega/drivers/cpu../ASF/common/boards../ASF/common/services/ioport../ASF/xmega/drivers/nvm../ASF/xmega/boards../ASF/xmega/utils../ASF/xmega/drivers/wdt../ASF/common/services/clock../ASF/common/services/delay../ASF/xmega/drivers/usart../ASF/xmega/services/pwm../ASF/common/drivers/nvm../ASF/common/services/serial/xmega_usart../ASF/common/services/serial../ASF/common/utils/stdio/stdio_serial../ASF/xmega/services/timeout../../../include../../../demo/object + -fdata-sections + True + True + True + -std=gnu99 -fno-strict-aliasing -Wstrict-prototypes -Wmissing-prototypes -Werror-implicit-function-declaration -Wpointer-arith -mrelax + libm + -Wl,--relax -Wl,--section-start=.BOOT=0x40000 + -mrelax -DBOARD=XMEGA_A3BU_XPLAINED -DIOPORT_XMEGA_COMPAT + ../ASF/xmega/drivers/rtc32../ASF/xmega/drivers/pmic../ASF/xmega/boards/xmega_a3bu_xplained../ASF/xmega/drivers/nvm../ASF/xmega/utils/preprocessor../ASF/common/utils../ASF/common/services/sleepmgr../ASF/xmega/drivers/sleep../ASF/common/services/gpio../ASF/xmega/drivers/tc../ASF/xmega/drivers/adc../ASF/xmega/drivers/cpu../ASF/common/boards../ASF/common/services/ioport../ASF/xmega/boards../ASF/xmega/utils../ASF/common/services/clock../ASF/common/services/delay../ASF/xmega/drivers/wdt../ASF/xmega/drivers/usart../config.../ASF/xmega/services/pwm../ASF/common/drivers/nvm../ASF/common/services/serial/xmega_usart../ASF/common/services/serial../ASF/common/utils/stdio/stdio_serial../ASF/xmega/services/timeout + Maximum (-g3) + Default (-Wa,-g) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + compile + + + compile + bacnet-stack\lighting.c + + + compile + bacnet-stack\proplist.c + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + bacnet-stack\s_cov.c + + + compile + bacnet-stack\s_rp.c + + + compile + bacnet-stack\s_whois.c + + + compile + bacnet-stack\s_wp.c + + + compile + bacnet-stack\cov.c + + + compile + bacnet-stack\tsm.c + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + + + compile + bacnet-stack\h_dcc.c + + + compile + bacnet-stack\h_npdu.c + + + compile + bacnet-stack\h_rd.c + + + compile + bacnet-stack\h_rp.c + + + compile + bacnet-stack\h_rpm.c + + + compile + bacnet-stack\h_whohas.c + + + compile + bacnet-stack\h_whois.c + + + compile + bacnet-stack\h_wp.c + + + compile + bacnet-stack\noserv.c + + + compile + bacnet-stack\s_iam.c + + + compile + bacnet-stack\s_ihave.c + + + compile + bacnet-stack\txbuf.c + + + compile + bacnet-stack\abort.c + + + compile + bacnet-stack\apdu.c + + + compile + bacnet-stack\bacaddr.c + + + compile + bacnet-stack\bacapp.c + + + compile + bacnet-stack\bacdcode.c + + + compile + bacnet-stack\bacerror.c + + + compile + bacnet-stack\bacint.c + + + compile + bacnet-stack\bacreal.c + + + compile + bacnet-stack\bacstr.c + + + compile + bacnet-stack\crc.c + + + compile + bacnet-stack\dcc.c + + + compile + bacnet-stack\fifo.c + + + compile + bacnet-stack\iam.c + + + compile + bacnet-stack\ihave.c + + + compile + bacnet-stack\memcopy.c + + + compile + bacnet-stack\npdu.c + + + compile + bacnet-stack\rd.c + + + compile + bacnet-stack\reject.c + + + compile + bacnet-stack\ringbuf.c + + + compile + bacnet-stack\rp.c + + + compile + bacnet-stack\rpm.c + + + compile + bacnet-stack\version.c + + + compile + bacnet-stack\whohas.c + + + compile + bacnet-stack\whois.c + + + compile + bacnet-stack\wp.c + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + + \ No newline at end of file diff --git a/bacnet-stack/ports/xplained/bacnet.h b/bacnet-stack/ports/xplained/bacnet.h new file mode 100644 index 00000000..42815f36 --- /dev/null +++ b/bacnet-stack/ports/xplained/bacnet.h @@ -0,0 +1,42 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACNET_H +#define BACNET_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + void bacnet_init (void); + void bacnet_task (void); + void bacnet_task_timed( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/xplained/bname.c b/bacnet-stack/ports/xplained/bname.c new file mode 100644 index 00000000..be526b33 --- /dev/null +++ b/bacnet-stack/ports/xplained/bname.c @@ -0,0 +1,281 @@ +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "nvmdata.h" +#include "device.h" +#include "bname.h" + +/************************************************************************* +* DESCRIPTION: Test the BACnet CharacterString for validity +* RETURN: true if valid +* NOTES: none +**************************************************************************/ +static bool bacnet_name_isvalid(uint8_t encoding, + uint8_t length, + char *str) +{ + bool valid = false; + + if ((encoding < MAX_CHARACTER_STRING_ENCODING) && + (length <= NVM_NAME_SIZE)) { + if (encoding == CHARACTER_UTF8) { + valid = utf8_isvalid(str, length); + } else { + valid = true; + } + } + + return valid; +} + +/************************************************************************* +* DESCRIPTION: Copy the name from non-volatile memory at offset +* RETURN: number of bytes read, or -1 on error +* NOTES: none +**************************************************************************/ +int bacnet_name_copy( + uint16_t offset, + uint8_t *dest, + uint8_t dest_len) +{ + uint8_t encoding = 0; + uint8_t length = 0; + char name[NVM_NAME_SIZE + 1] = ""; + unsigned i = 0; + int bytes_read = -1; + + nvm_read(NVM_NAME_ENCODING(offset), &encoding, 1); + nvm_read(NVM_NAME_LENGTH(offset), &length, 1); + nvm_read(NVM_NAME_STRING(offset), + (uint8_t *) & name, NVM_NAME_SIZE); + if (bacnet_name_isvalid(encoding, length, name)) { + if (dest_len > NVM_NAME_SIZE) { + dest_len = NVM_NAME_SIZE; + } + bytes_read = dest_len; + for (i = 0; i < dest_len; i++) { + if (i < length) { + dest[i] = name[i]; + } else { + dest[i] = 0; + } + } + } else { + for (i = 0; i < dest_len; i++) { + dest[i] = 0; + } + } + + return bytes_read; +} + +/************************************************************************* +* DESCRIPTION: Encode the name in a buffer in the sequence stored in EEPROM. +* RETURN: number of bytes in buffer, or 0 if too big to fit. +* NOTES: none +**************************************************************************/ +uint8_t bacnet_name_encode( + uint8_t *buffer, + uint8_t buffer_len, + uint8_t encoding, + char *str, + uint8_t str_len) +{ + unsigned len = 0; + unsigned i = 0; + + if (str_len < (255-2)) { + len = 1 + 1 + str_len; + if (len <= buffer_len) { + buffer[NVM_NAME_LENGTH(0)] = str_len; + buffer[NVM_NAME_ENCODING(0)] = encoding; + for (i = 0; i < str_len; i++) { + buffer[NVM_NAME_STRING(0)+i] = str[i]; + } + } else { + len = 0; + } + } + + return len; +} + +/************************************************************************* +* DESCRIPTION: Store the name to non-volatile memory at offset +* RETURN: true if name is a valid set of characters +* NOTES: none +**************************************************************************/ +bool bacnet_name_save( + uint16_t offset, + uint8_t encoding, + char *str, + uint8_t str_len) +{ + uint8_t buffer[NVM_NAME_SIZE] = { 0 }; + uint8_t length = 0; + + if (bacnet_name_isvalid(encoding, str_len, str)) { + length = bacnet_name_encode( + buffer, + sizeof(buffer), + encoding, + str, + str_len); + if (length) { + nvm_write( + offset, + &buffer[0],length); + return true; + } + } + + return false; +} + +bool bacnet_name_set(uint16_t offset, + BACNET_CHARACTER_STRING * char_string) +{ + uint8_t encoding = 0; + uint8_t length = 0; + char *str = NULL; + + length = characterstring_length(char_string); + encoding = characterstring_encoding(char_string); + str = characterstring_value(char_string); + return bacnet_name_save(offset, encoding, str, length); +} + +bool bacnet_name_write_unique(uint16_t offset, + int object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; + size_t length = 0; + uint8_t encoding = 0; + int duplicate_type = 0; + uint32_t duplicate_instance = 0; + + length = characterstring_length(char_string); + if (length < 1) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else if (length <= NVM_NAME_SIZE) { + encoding = characterstring_encoding(char_string); + if (encoding < MAX_CHARACTER_STRING_ENCODING) { + if (Device_Valid_Object_Name(char_string, &duplicate_type, + &duplicate_instance)) { + if ((duplicate_type == object_type) && + (duplicate_instance == object_instance)) { + /* writing same name to same object */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_DUPLICATE_NAME; + } + } else { + status = bacnet_name_set(offset, char_string); + if (status) { + Device_Inc_Database_Revision(); + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + + return status; +} + +/* no required minumum length or duplicate checking */ +bool bacnet_name_write(uint16_t offset, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; + size_t length = 0; + uint8_t encoding = 0; + + length = characterstring_length(char_string); + if (length <= NVM_NAME_SIZE) { + encoding = characterstring_encoding(char_string); + if (encoding < MAX_CHARACTER_STRING_ENCODING) { + status = bacnet_name_set(offset, char_string); + if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + + return status; +} + +void bacnet_name_init(uint16_t offset, + char *default_string) +{ + (void) bacnet_name_save(offset, CHARACTER_UTF8, default_string, + strlen(default_string)); +} + +void bacnet_name(uint16_t offset, + BACNET_CHARACTER_STRING * char_string, + char *default_string) +{ + uint8_t encoding = 0; + uint8_t length = 0; + char name[NVM_NAME_SIZE + 1] = ""; + + nvm_read(NVM_NAME_ENCODING(offset), &encoding, 1); + nvm_read(NVM_NAME_LENGTH(offset), &length, 1); + nvm_read(NVM_NAME_STRING(offset), (uint8_t *) & name[0], NVM_NAME_SIZE); + if (bacnet_name_isvalid(encoding, length, name)) { + characterstring_init(char_string, encoding, &name[0], length); + } else if (default_string) { + characterstring_init_ansi(char_string, default_string); + } +} diff --git a/bacnet-stack/ports/xplained/bname.h b/bacnet-stack/ports/xplained/bname.h new file mode 100644 index 00000000..0a96a5fe --- /dev/null +++ b/bacnet-stack/ports/xplained/bname.h @@ -0,0 +1,76 @@ +/************************************************************************** +* +* Copyright (C) 2010 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BACNET_NAME_H +#define BACNET_NAME_H + +#include +#include "bacstr.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int bacnet_name_copy( + uint16_t offset, + uint8_t *dest, + uint8_t dest_len); + bool bacnet_name_set( + uint16_t eeprom_offset, + BACNET_CHARACTER_STRING * char_string); + void bacnet_name_init( + uint16_t eeprom_offset, + char *default_string); + uint8_t bacnet_name_encode( + uint8_t *buffer, + uint8_t buffer_len, + uint8_t encoding, + char *str, + uint8_t str_len); + bool bacnet_name_save( + uint16_t offset, + uint8_t encoding, + char *str, + uint8_t str_len); + void bacnet_name( + uint16_t eeprom_offset, + BACNET_CHARACTER_STRING * char_string, + char *default_string); + bool bacnet_name_write_unique( + uint16_t offset, + int object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + /* no required minumum length or duplicate checking */ + bool bacnet_name_write( + uint16_t offset, + BACNET_CHARACTER_STRING * char_string, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/xplained/config/conf_adc.h b/bacnet-stack/ports/xplained/config/conf_adc.h new file mode 100644 index 00000000..f748a9c3 --- /dev/null +++ b/bacnet-stack/ports/xplained/config/conf_adc.h @@ -0,0 +1,49 @@ +/** + * \file + * + * \brief Chip-specific ADC configuration + * + * Copyright (c) 2011 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef CONF_ADC_H +#define CONF_ADC_H + +#define CONFIG_ADC_CALLBACK_ENABLE +#define CONFIG_ADC_CALLBACK_TYPE uint16_t + +#endif /* CONF_ADC_H */ diff --git a/bacnet-stack/ports/xplained/config/conf_board.h b/bacnet-stack/ports/xplained/config/conf_board.h new file mode 100644 index 00000000..a483c6e2 --- /dev/null +++ b/bacnet-stack/ports/xplained/config/conf_board.h @@ -0,0 +1,51 @@ +/** + * \file + * + * \brief Board configuration + * + * Copyright (c) 2011 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef CONF_BOARD_H_INCLUDED +#define CONF_BOARD_H_INCLUDED + +#define CONF_BOARD_C12832A1Z + +// Enable AT45DBX Component. +#define CONF_BOARD_AT45DBX + +#endif /* CONF_BOARD_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/config/conf_clock.h b/bacnet-stack/ports/xplained/config/conf_clock.h new file mode 100644 index 00000000..bd9fd2e4 --- /dev/null +++ b/bacnet-stack/ports/xplained/config/conf_clock.h @@ -0,0 +1,60 @@ +/** + * \file + * + * \brief Clock system configuration + * + * Copyright (c) 2011-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef CONF_CLOCK_H_INCLUDED +#define CONF_CLOCK_H_INCLUDED + +//! Configuration using On-Chip RC oscillator at 48MHz +//! The RC oscillator is calibrated via USB Start Of Frame +//! Clk USB = 48MHz (used by USB) +//! Clk sys = 48MHz +//! Clk cpu/per = 24MHz +#define CONFIG_USBCLK_SOURCE USBCLK_SRC_RCOSC +#define CONFIG_OSC_RC32_CAL 48000000UL + +#define CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC OSC_ID_USBSOF + +#define CONFIG_SYSCLK_SOURCE SYSCLK_SRC_RC32MHZ +#define CONFIG_SYSCLK_PSADIV SYSCLK_PSADIV_2 +#define CONFIG_SYSCLK_PSBCDIV SYSCLK_PSBCDIV_1_1 + +#endif /* CONF_CLOCK_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/config/conf_nvm.h b/bacnet-stack/ports/xplained/config/conf_nvm.h new file mode 100644 index 00000000..2892087f --- /dev/null +++ b/bacnet-stack/ports/xplained/config/conf_nvm.h @@ -0,0 +1,47 @@ +/** + * \file + * + * \brief Non volatile memories management for XMEGA devices + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#ifndef CONF_NVM_H_INCLUDED +#define CONF_NVM_H_INCLUDED + +#endif /* CONF_NVM_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/config/conf_rtc32.h b/bacnet-stack/ports/xplained/config/conf_rtc32.h new file mode 100644 index 00000000..215b3700 --- /dev/null +++ b/bacnet-stack/ports/xplained/config/conf_rtc32.h @@ -0,0 +1,49 @@ +/** + * \file + * + * \brief RTC32 configuration + * + * Copyright (c) 2011 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef CONF_RTC32_H +#define CONF_RTC32_H + +//#define CONFIG_RTC32_COMPARE_INT_LEVEL RTC32_COMPINTLVL_LO_gc +//#define CONFIG_RTC32_CLOCK_1024HZ + +#endif /* CONF_RTC32_H */ diff --git a/bacnet-stack/ports/xplained/config/conf_sleepmgr.h b/bacnet-stack/ports/xplained/config/conf_sleepmgr.h new file mode 100644 index 00000000..659cbce4 --- /dev/null +++ b/bacnet-stack/ports/xplained/config/conf_sleepmgr.h @@ -0,0 +1,48 @@ +/** + * \file + * + * \brief Chip-specific sleep manager configuration + * + * Copyright (c) 2011 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef CONF_SLEEPMGR_H +#define CONF_SLEEPMGR_H + +#define CONFIG_SLEEPMGR_ENABLE + +#endif /* CONF_SLEEPMGR_H */ diff --git a/bacnet-stack/ports/xplained/config/conf_timeout.h b/bacnet-stack/ports/xplained/config/conf_timeout.h new file mode 100644 index 00000000..e13152e5 --- /dev/null +++ b/bacnet-stack/ports/xplained/config/conf_timeout.h @@ -0,0 +1,58 @@ +/** + * \file + * + * \brief Configuration file for timeout service + * + * Copyright (C) 2011 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef CONF_TIMEOUT_H +#define CONF_TIMEOUT_H + +// For A3B devices with RTC32 module +#define CLOCK_SOURCE_RTC32 + +//! Define clock frequency +#define TIMEOUT_CLOCK_SOURCE_HZ 1024 + +//! Configure timeout channels +#define TIMEOUT_COUNT 1 + +//! Tick frequency +#define TIMEOUT_TICK_HZ 1 + +#endif /* CONF_TIMEOUT_H */ diff --git a/bacnet-stack/ports/xplained/config/conf_twim.h b/bacnet-stack/ports/xplained/config/conf_twim.h new file mode 100644 index 00000000..85540235 --- /dev/null +++ b/bacnet-stack/ports/xplained/config/conf_twim.h @@ -0,0 +1,49 @@ +/** + * \file + * + * \brief TWIM Configuration File for AVR XMEGA. + * + * Copyright (c) 2011 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +#ifndef _CONF_TWIM_H_ +#define _CONF_TWIM_H_ + +#define CONF_TWIM_INTLVL TWI_MASTER_INTLVL_MED_gc +#define CONF_PMIC_INTLVL PMIC_MEDLVLEN_bm + +#endif // _CONF_TWIM_H_ diff --git a/bacnet-stack/ports/xplained/config/conf_usart_serial.h b/bacnet-stack/ports/xplained/config/conf_usart_serial.h new file mode 100644 index 00000000..d01f182c --- /dev/null +++ b/bacnet-stack/ports/xplained/config/conf_usart_serial.h @@ -0,0 +1,47 @@ +/** + * \file ********************************************************************* + * + * \brief USART Serial configuration + * + * Copyright (c) 2011 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#ifndef CONF_USART_SERIAL_H_INCLUDED +#define CONF_USART_SERIAL_H_INCLUDED + +#endif /* CONF_USART_SERIAL_H_INCLUDED */ diff --git a/bacnet-stack/ports/xplained/device.c b/bacnet-stack/ports/xplained/device.c new file mode 100644 index 00000000..50b928d1 --- /dev/null +++ b/bacnet-stack/ports/xplained/device.c @@ -0,0 +1,952 @@ +/************************************************************************** +* +* Copyright (C) 2015 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "apdu.h" +#include "dcc.h" +#include "datalink.h" +#include "rs485.h" +#include "version.h" +#include "nvmdata.h" +#include "handlers.h" +#include "bname.h" +#include "stack.h" +#include "nvmdata.h" +/* objects */ +#include "device.h" +#include "ai.h" + +/* forward prototype */ +int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA * rpdata); +bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA * wp_data); + +static struct my_object_functions { + BACNET_OBJECT_TYPE Object_Type; + object_init_function Object_Init; + object_count_function Object_Count; + object_index_to_instance_function Object_Index_To_Instance; + object_valid_instance_function Object_Valid_Instance; + object_name_function Object_Name; + read_property_function Object_Read_Property; + write_property_function Object_Write_Property; + rpm_property_lists_function Object_RPM_List; + object_value_list_function Object_Value_List; + object_cov_function Object_COV; + object_cov_clear_function Object_COV_Clear; +} Object_Table[] = { + { + OBJECT_DEVICE, NULL, + Device_Count, Device_Index_To_Instance, + Device_Valid_Object_Instance_Number, Device_Object_Name, + Device_Read_Property_Local, Device_Write_Property_Local, + Device_Property_Lists, + NULL, NULL, NULL}, { + OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count, + Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance, + Analog_Input_Object_Name, Analog_Input_Read_Property, + Analog_Input_Write_Property, Analog_Input_Property_Lists, + NULL, NULL, NULL}, { + MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL} +}; + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static uint32_t Database_Revision; +static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Device_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_REVISION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_OBJECT_LIST, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_DEVICE_ADDRESS_BINDING, + PROP_DATABASE_REVISION, + -1 +}; + +static const int Device_Properties_Optional[] = { + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DESCRIPTION, + PROP_LOCATION, + -1 +}; + +static const int Device_Properties_Proprietary[] = { + -1 +}; + +static struct my_object_functions + *Device_Objects_Find_Functions(BACNET_OBJECT_TYPE Object_Type) +{ + struct my_object_functions *pObject = NULL; + + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + /* handle each object type */ + if (pObject->Object_Type == Object_Type) { + return (pObject); + } + pObject++; + } + + return (NULL); +} + +/* Encodes the property APDU and returns the length, + or sets the error, and returns BACNET_STATUS_ERROR */ +int Device_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + pObject = Device_Objects_Find_Functions(rpdata->object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(rpdata->object_instance)) { + if (pObject->Object_Read_Property) { + apdu_len = pObject->Object_Read_Property(rpdata); + } + } + } + + return apdu_len; +} + +bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = Device_Objects_Find_Functions(wp_data->object_type); + if (pObject) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(wp_data->object_instance)) { + if (pObject->Object_Write_Property) { + status = pObject->Object_Write_Property(wp_data); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return status; +} + +/* for a given object type, returns the special property list */ +void Device_Objects_Property_List(BACNET_OBJECT_TYPE object_type, + struct special_property_list_t *pPropertyList) +{ + struct my_object_functions *pObject = NULL; + + pPropertyList->Required.pList = NULL; + pPropertyList->Optional.pList = NULL; + pPropertyList->Proprietary.pList = NULL; + + /* If we can find an entry for the required object type + * and there is an Object_List_RPM fn ptr then call it + * to populate the pointers to the individual list counters. + */ + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { + pObject->Object_RPM_List(&pPropertyList->Required.pList, + &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); + } + + /* Fetch the counts if available otherwise zero them */ + pPropertyList->Required.count = + pPropertyList->Required.pList == + NULL ? 0 : property_list_count(pPropertyList->Required.pList); + + pPropertyList->Optional.count = + pPropertyList->Optional.pList == + NULL ? 0 : property_list_count(pPropertyList->Optional.pList); + + pPropertyList->Proprietary.count = + pPropertyList->Proprietary.pList == + NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList); + + return; +} + +void Device_Property_Lists(const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Device_Properties_Required; + if (pOptional) + *pOptional = Device_Properties_Optional; + if (pProprietary) + *pProprietary = Device_Properties_Proprietary; + + return; +} + +unsigned Device_Count(void) +{ + return 1; +} + +uint32_t Device_Index_To_Instance(unsigned index) +{ + index = index; + return Object_Instance_Number; +} + +static char *Device_Name_Default( + void) +{ + static char text_string[32]; /* okay for single thread */ + + sprintf(text_string, "DEVICE-%lu", Object_Instance_Number); + + return text_string; +} + +bool Device_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + + if (object_instance == Object_Instance_Number) { + bacnet_name(NVM_DEVICE_NAME, object_name, Device_Name_Default()); + status = true; + } + + return status; +} + +const char *Device_Model_Name(void) +{ + return "XMEGA-A3BU Xplained"; +} + +const char *Device_Vendor_Name(void) +{ + return BACNET_VENDOR_NAME; +} + +const char *Device_Firmware_Revision(void) +{ + return "1.0"; +} + +const char *Device_Application_Software_Version(void) +{ + return BACNET_VERSION_TEXT; +} + +bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA * rd_data) +{ + bool status = false; + + if (characterstring_ansi_same(&rd_data->password, "filister")) { + Reinitialize_State = rd_data->state; + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + /* Note: you could use a mix of state + and password to multiple things */ + /* note: you probably want to restart *after* the + simple ack has been sent from the return handler + so just set a flag from here */ + status = true; + } else { + rd_data->error_class = ERROR_CLASS_SECURITY; + rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; + } + + return status; +} + +BACNET_REINITIALIZED_STATE Device_Reinitialized_State(void) +{ + return Reinitialize_State; +} + +void Device_Init(object_functions_t * object_table) +{ + struct my_object_functions *pObject = NULL; + + /* we don't use the object table passed in + since there is extra stuff we don't need in there. */ + (void) object_table; + /* our local object table */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Init) { + pObject->Object_Init(); + } + pObject++; + } + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number(void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number(uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + if (object_id != Object_Instance_Number) { + Device_Inc_Database_Revision(); + Object_Instance_Number = object_id; + } + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number(uint32_t object_id) +{ + return (Object_Instance_Number == object_id); +} + +BACNET_DEVICE_STATUS Device_System_Status(void) +{ + return System_Status; +} + +int Device_Set_System_Status(BACNET_DEVICE_STATUS status, + bool local) +{ + /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ + int result = -1; + + if (status < MAX_DEVICE_STATUS) { + System_Status = status; + result = 0; + } + + return result; +} + +uint16_t Device_Vendor_Identifier(void) +{ + return BACNET_VENDOR_ID; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported(void) +{ + return SEGMENTATION_NONE; +} + +uint32_t Device_Database_Revision(void) +{ + return Database_Revision; +} + +void Device_Inc_Database_Revision(void) +{ + Database_Revision++; +} + +bool Device_Encode_Value_List( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list) +{ + bool status = false; /* Ever the pessamist! */ + struct my_object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_Value_List) { + status = pObject->Object_Value_List( + object_instance, + value_list); + } + } + } + + return (status); +} + +bool Device_COV( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance) +{ + bool status = false; /* Ever the pessamist! */ + struct my_object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_COV) { + status = pObject->Object_COV( + object_instance); + } + } + } + + return (status); +} + +void Device_COV_Clear( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance) +{ + struct my_object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_COV_Clear) { + pObject->Object_COV_Clear(object_instance); + } + } + } +} + +bool Device_Value_List_Supported( + BACNET_OBJECT_TYPE object_type) +{ + bool status = false; /* Ever the pessimist! */ + struct my_object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Value_List) { + status = true; + } + } + + return (status); +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +unsigned Device_Object_List_Count(void) +{ + unsigned count = 0; /* number of objects */ + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + count += pObject->Object_Count(); + } + pObject++; + } + + return count; +} + +bool Device_Object_List_Identifier(unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned count = 0; + unsigned object_index = 0; + struct my_object_functions *pObject = NULL; + + /* array index zero is length - so invalid */ + if (array_index == 0) { + return status; + } + object_index = array_index - 1; + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count && pObject->Object_Index_To_Instance) { + object_index -= count; + count = pObject->Object_Count(); + if (object_index < count) { + *object_type = pObject->Object_Type; + *instance = pObject->Object_Index_To_Instance(object_index); + status = true; + break; + } + } + pObject++; + } + + return status; +} + +bool Device_Valid_Object_Name(BACNET_CHARACTER_STRING * object_name1, + int *object_type, + uint32_t * object_instance) +{ + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + BACNET_CHARACTER_STRING object_name2; + struct my_object_functions *pObject = NULL; + + max_objects = Device_Object_List_Count(); + for (i = 1; i <= max_objects; i++) { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) { + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE) type); + if ((pObject != NULL) && (pObject->Object_Name != NULL) && + (pObject->Object_Name(instance, &object_name2) && + characterstring_same(object_name1, &object_name2))) { + found = true; + if (object_type) { + *object_type = type; + } + if (object_instance) { + *object_instance = instance; + } + break; + } + } + } + + return found; +} + +bool Device_Valid_Object_Id(int object_type, + uint32_t object_instance) +{ + bool status = false; /* return value */ + struct my_object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE) object_type); + if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { + status = pObject->Object_Valid_Instance(object_instance); + } + + return status; +} + +bool Device_Object_Name_Copy(BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + struct my_object_functions *pObject = NULL; + bool found = false; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Name != NULL)) { + found = pObject->Object_Name(object_instance, object_name); + } + + return found; +} + +/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error */ +int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string = { 0 }; + BACNET_CHARACTER_STRING char_string = { 0 }; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + uint8_t *apdu = NULL; + struct my_object_functions *pObject = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch ((int) rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], rpdata->object_type, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Device_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], rpdata->object_type); + break; + case PROP_DESCRIPTION: + bacnet_name(NVM_DEVICE_DESCRIPTION, &char_string, + "default description"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + bacnet_name(NVM_DEVICE_LOCATION, &char_string, + "default location"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_System_Status()); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, Device_Vendor_Name()); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = encode_application_unsigned(&apdu[0], BACNET_VENDOR_ID); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, Device_Model_Name()); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, + Device_Firmware_Revision()); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, + Device_Application_Software_Version()); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + BACNET_PROTOCOL_REVISION); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* set the object types with objects to supported */ + i = 0; + pObject = &Object_Table[i]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { + bitstring_set_bit(&bit_string, pObject->Object_Type, true); + } + pObject++; + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (rpdata->array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + /* Abort response */ + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } else { + /* error: internal error? */ + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_OTHER; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (Device_Object_List_Identifier(rpdata->array_index, + &object_type, &instance)) + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Database_Revision()); + break; + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; + case 512: + apdu_len = encode_application_unsigned(&apdu[0], stack_size()); + break; + case 513: + apdu_len = encode_application_unsigned(&apdu[0], stack_unused()); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value - false=error */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + uint8_t max_master = 0; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_OBJECT_LIST) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch ((int) wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type.Object_Id. + instance))) { + nvm_write(NVM_DEVICE_0, + (uint8_t *) & value.type.Object_Id.instance, 4); + /* we could send an I-Am broadcast to let the world know */ + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_INFO_FRAMES: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int <= 255) { + dlmstp_set_max_info_frames(value.type.Unsigned_Int); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_MASTER: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= 127)) { + max_master = value.type.Unsigned_Int; + dlmstp_set_max_master(max_master); + nvm_write(NVM_MAX_MASTER, &max_master, 1); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + status = + bacnet_name_write_unique(NVM_DEVICE_NAME, + wp_data->object_type, wp_data->object_instance, + &value.type.Character_String, &wp_data->error_class, + &wp_data->error_code); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_DESCRIPTION: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + status = + bacnet_name_write(NVM_DEVICE_DESCRIPTION, + &value.type.Character_String, &wp_data->error_class, + &wp_data->error_code); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_LOCATION: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + status = + bacnet_name_write(NVM_DEVICE_LOCATION, + &value.type.Character_String, &wp_data->error_class, + &wp_data->error_code); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_TYPE: + case PROP_VENDOR_NAME: + case PROP_FIRMWARE_REVISION: + case PROP_APPLICATION_SOFTWARE_VERSION: + case PROP_DAYLIGHT_SAVINGS_STATUS: + case PROP_PROTOCOL_VERSION: + case PROP_PROTOCOL_REVISION: + case PROP_PROTOCOL_SERVICES_SUPPORTED: + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + case PROP_OBJECT_LIST: + case PROP_MAX_APDU_LENGTH_ACCEPTED: + case PROP_SEGMENTATION_SUPPORTED: + case PROP_DEVICE_ADDRESS_BINDING: + case PROP_DATABASE_REVISION: + case 512: + case 513: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} diff --git a/bacnet-stack/ports/xplained/dlmstp.c b/bacnet-stack/ports/xplained/dlmstp.c new file mode 100644 index 00000000..8e2b3b70 --- /dev/null +++ b/bacnet-stack/ports/xplained/dlmstp.c @@ -0,0 +1,1459 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2010 Steve Karg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include "bacdef.h" +#include "dlmstp.h" +#include "mstpdef.h" +#include "rs485.h" +#include "crc.h" +#include "npdu.h" +#include "bits.h" +#include "bytes.h" +#include "bacaddr.h" +#include "ringbuf.h" +#include "timer.h" + +/* This file has been customized for use with small microprocessors */ +/* Assumptions: + Only one MS/TP datalink layer +*/ + +/* count must be a power of 2 for ringbuf library */ +#ifndef MSTP_PDU_PACKET_COUNT +#define MSTP_PDU_PACKET_COUNT 2 +#endif + +/* The state of the Receive State Machine */ +static volatile MSTP_RECEIVE_STATE Receive_State; +/* When a master node is powered up or reset, */ +/* it shall unconditionally enter the INITIALIZE state. */ +static volatile MSTP_MASTER_STATE Master_State; +/* bit-sized boolean flags */ +static volatile struct mstp_flag_t { + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an invalid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* set to TRUE when we get a frame not for us */ + unsigned ReceivedValidFrameNotForUs:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* A Boolean flag set TRUE by the datalink if a + packet has been received, but not processed. */ + unsigned ReceivePacketPending:1; +} MSTP_Flag; + +/* Used to store the data length of a received frame. */ +static volatile uint32_t DataLength; +/* Used to store the destination address of a received frame. */ +static volatile uint8_t DestinationAddress; +/* Used to count the number of received octets or errors. */ +/* This is used in the detection of link activity. */ +/* Compared to Nmin_octets */ +static volatile uint8_t EventCount; +/* Used to store the frame type of a received frame. */ +static volatile uint8_t FrameType; +/* An array of octets, used to store octets as they are received. */ +/* InputBuffer is indexed from 0 to InputBufferSize-1. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *InputBuffer; +static volatile uint16_t InputBufferSize; +/* Used to store the Source Address of a received frame. */ +static volatile uint8_t SourceAddress; +/* "This Station," the MAC address of this node. TS is generally read from a */ +/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ +/* 0 to 254. The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for TS. */ +static volatile uint8_t This_Station; +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +static volatile uint8_t Nmax_info_frames = MSTP_PDU_PACKET_COUNT; +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +static volatile uint8_t Nmax_master = 127; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#define Treply_timeout 260 + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#define Tusage_timeout 60 + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +#define Treply_delay 240 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +/* data structure for MS/TP PDU Queue */ +struct mstp_pdu_packet { + bool data_expecting_reply; + uint8_t destination_mac; + uint16_t length; + uint8_t buffer[MAX_MPDU]; +}; +static volatile struct mstp_pdu_packet PDU_Buffer[MSTP_PDU_PACKET_COUNT]; +static RING_BUFFER PDU_Queue; + +bool dlmstp_init(char *ifname) +{ + ifname = ifname; + + Ringbuf_Init(&PDU_Queue, (uint8_t *) & PDU_Buffer, + sizeof(struct mstp_pdu_packet), MSTP_PDU_PACKET_COUNT); + + return true; +} + +void dlmstp_cleanup(void) +{ + /* nothing to do for static buffers */ +} + +void dlmstp_fill_bacnet_address(BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +static bool dlmstp_compare_data_expecting_reply(uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + uint8_t dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset + 5]; + else + request.service_choice = request_pdu[offset + 3]; + /* decode the reply data */ + reply.address.mac[0] = dest_address; + reply.address.mac_len = 1; + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 5]; + else + reply.service_choice = reply_pdu[offset + 3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 4]; + else + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + if (request.invoke_id != reply.invoke_id) { + return false; + } + /* these services don't have service choice included */ + if ((reply.pdu_type != PDU_TYPE_REJECT) && + (reply.pdu_type != PDU_TYPE_ABORT)) { + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } + if (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ +static void MSTP_Send_Frame(uint8_t frame_type, /* type of frame to send - see defines */ + + uint8_t destination, /* destination address */ + + uint8_t source, /* source address */ + + uint8_t * data, /* any data to be sent - may be null */ + + uint16_t data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + uint8_t buffer[8]; /* stores the header and header crc */ + uint8_t buffer_crc[2]; /* stores the data crc */ + uint16_t i = 0; /* used to calculate CRC for data */ + + /* create the MS/TP header */ + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = HI_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = LO_BYTE(data_len); + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + if (data_len) { + /* calculate CRC for any data */ + for (i = 0; i < data_len; i++) { + crc16 = CRC_Calc_Data(data[i], crc16); + } + crc16 = ~crc16; + buffer_crc[0] = (crc16 & 0x00FF); + buffer_crc[1] = ((crc16 & 0xFF00) >> 8); + } + /* on a slower processor, we don't want to calculate + the CRC after we send the header because there + will be a gap */ + rs485_bytes_send(buffer, 8); + if (data_len) { + rs485_bytes_send(data, data_len); + rs485_bytes_send(buffer_crc, 2); + } +} + +static void MSTP_Receive_Frame_FSM(void) +{ + /* stores the latest received data octet */ + uint8_t DataRegister = 0; + /* Used to accumulate the CRC on the data field of a frame. */ + static uint16_t DataCRC = 0; + /* Used to accumulate the CRC on the header of a frame. */ + static uint8_t HeaderCRC = 0; + /* Used as an index by the Receive State Machine, + up to a maximum value of the MPDU */ + static uint16_t Index = 0; + + switch (Receive_State) { + case MSTP_RECEIVE_STATE_IDLE: + /* In the IDLE state, the node waits + for the beginning of a frame. */ + if (rs485_receive_error()) { + /* EatAnError */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0x55) { + /* Preamble1 */ + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } + } + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + /* In the PREAMBLE state, the node waits for the + second octet of the preamble. */ + if (rs485_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0xFF) { + /* Preamble2 */ + Index = 0; + HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_HEADER; + } else if (DataRegister == 0x55) { + /* ignore RepeatedPreamble1 */ + /* wait for the second preamble octet. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } else { + /* NotPreamble */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_HEADER: + /* In the HEADER state, the node waits + for the fixed message header. */ + if (rs485_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index == 0) { + /* FrameType */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + FrameType = DataRegister; + Index = 1; + } else if (Index == 1) { + /* Destination */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DestinationAddress = DataRegister; + Index = 2; + } else if (Index == 2) { + /* Source */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + SourceAddress = DataRegister; + Index = 3; + } else if (Index == 3) { + /* Length1 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength = DataRegister * 256; + Index = 4; + } else if (Index == 4) { + /* Length2 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength += DataRegister; + Index = 5; + } else if (Index == 5) { + /* HeaderCRC */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + /* In the HEADER_CRC state, the node validates the CRC + on the fixed message header. */ + if (HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + if (DataLength == 0) { + /* NoData */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with + no data has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs */ + MSTP_Flag.ReceivedValidFrameNotForUs = true; + } + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + /* receive the data portion of the frame. */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + if (DataLength <= InputBufferSize) { + /* Data */ + Receive_State = MSTP_RECEIVE_STATE_DATA; + } else { + /* FrameTooLong */ + Receive_State = + MSTP_RECEIVE_STATE_SKIP_DATA; + } + } else { + /* NotForUs */ + Receive_State = MSTP_RECEIVE_STATE_SKIP_DATA; + } + Index = 0; + DataCRC = 0xFFFF; + } + } + } else { + /* indicate that an error has occurred during */ + /* the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_DATA: + case MSTP_RECEIVE_STATE_SKIP_DATA: + /* In the DATA state, the node waits + for the data portion of a frame. */ + if (rs485_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index < DataLength) { + /* DataOctet */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + if (Index < InputBufferSize) { + InputBuffer[Index] = DataRegister; + } + Index++; + } else if (Index == DataLength) { + /* CRC1 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + Index++; + } else if (Index == (DataLength + 1)) { + /* CRC2 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (DataCRC == 0xF0B8) { + if (Receive_State == MSTP_RECEIVE_STATE_DATA) { + /* ForUs */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs */ + MSTP_Flag.ReceivedValidFrameNotForUs = true; + } + + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + } + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + default: + /* shouldn't get here - but if we do... */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + break; + } + + return; +} + +#ifdef MSTP_DEBUG_STATES +static MSTP_MASTER_STATE Master_State_Log[128]; +static unsigned master_state_log_index = 0; +void log_master_state(MSTP_MASTER_STATE state) +{ + Master_State_Log[master_state_log_index] = state; + master_state_log_index++; + if (master_state_log_index > 128) { + master_state_log_index = 0; + } +} +#else +#define log_master_state(n) (void)n; +#endif + +static void MSTP_Slave_Node_FSM(void) +{ + /* packet from the PDU Queue */ + struct mstp_pdu_packet *pkt; + + if (MSTP_Flag.ReceivedInvalidFrame) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + MSTP_Flag.ReceivedInvalidFrame = false; + } else if (MSTP_Flag.ReceivedValidFrame) { + switch (FrameType) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + if (pkt != NULL) { + uint8_t frame_type; + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = + FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, + This_Station, (uint8_t *) & pkt->buffer[0], + pkt->length); + Master_State = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + /* clear the queue */ + (void) Ringbuf_Pop(&PDU_Queue, NULL); + } else if (rs485_silence_elapsed(Treply_delay)) { + /* If no reply will be available from the higher layers + within Treply_delay after the reception of the final + octet of the requesting frame (the mechanism used + to determine this is a local matter), then no reply + is possible. */ + MSTP_Flag.ReceivedValidFrame = false; + } + } else { + /* no reply when addressed as Broadcast */ + MSTP_Flag.ReceivedValidFrame = false; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, + SourceAddress, This_Station, &InputBuffer[0], + DataLength); + break; + case FRAME_TYPE_TOKEN: + case FRAME_TYPE_POLL_FOR_MASTER: + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + MSTP_Flag.ReceivedValidFrame = false; + break; + } + } +} + +/* returns true if we need to transition immediately */ +static bool MSTP_Master_Node_FSM(void) +{ + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + static uint8_t FrameCount; + /* "Next Station," the MAC address of the node to which This Station + passes the token. If the Next_Station is unknown, Next_Station shall + be equal to This_Station. */ + static uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + static uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + static unsigned RetryCount; + /* The number of tokens received by this node. When this counter reaches */ + /* the value Npoll, the node polls the address range between TS and NS */ + /* for additional master nodes. TokenCount is set to zero at the end of */ + /* the polling process. */ + static unsigned TokenCount; + /* next-x-station calculations */ + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + /* timeout values */ + uint16_t my_timeout = 10, ns_timeout = 0; + bool matched = false; + /* transition immediately to the next state */ + bool transition_now = false; + /* packet from the PDU Queue */ + struct mstp_pdu_packet *pkt; + + /* some calculations that several states need */ + next_poll_station = (Poll_Station + 1) % (Nmax_master + 1); + next_this_station = (This_Station + 1) % (Nmax_master + 1); + next_next_station = (Next_Station + 1) % (Nmax_master + 1); + log_master_state(Master_State); + switch (Master_State) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + Next_Station = This_Station; + Poll_Station = This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + TokenCount = Npoll; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + case MSTP_MASTER_STATE_IDLE: + /* In the IDLE state, the node waits for a frame. */ + if (rs485_silence_elapsed(Tno_token)) { + /* LostToken */ + /* assume that the token has been lost */ + EventCount = 0; /* Addendum 135-2004d-8 */ + /* set the receive frame flags to false in case we received + some bytes and had a timeout for some reason */ + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedInvalidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + Master_State = MSTP_MASTER_STATE_NO_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + MSTP_Flag.ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (MSTP_Flag.ReceivedValidFrame == true) { + switch (FrameType) { + case FRAME_TYPE_TOKEN: + /* ReceivedToken */ + /* tokens can't be broadcast */ + if (DestinationAddress == MSTP_BROADCAST_ADDRESS) + break; + MSTP_Flag.ReceivedValidFrame = false; + FrameCount = 0; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + /* ReceivedPFM */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + SourceAddress, This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* indicate successful reception to higher layers */ + MSTP_Flag.ReceivePacketPending = true; + /* broadcast DER just remains IDLE */ + if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { + Master_State = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, + SourceAddress, This_Station, &InputBuffer[0], + DataLength); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for + reference, and the flag will be cleared in the next state */ + if (Master_State != MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { + MSTP_Flag.ReceivedValidFrame = false; + } + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + /* Note: We could wait for up to Tusage_delay */ + if (Ringbuf_Empty(&PDU_Queue)) { + /* NothingToSend */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else { + uint8_t frame_type; + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, + (uint8_t *) & pkt->buffer[0], pkt->length); + FrameCount++; + switch (frame_type) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (pkt->destination_mac == MSTP_BROADCAST_ADDRESS) + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + (void) Ringbuf_Pop(&PDU_Queue, NULL); + } + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + if (rs485_silence_elapsed(Treply_timeout)) { + /* ReplyTimeout */ + /* assume that the request has failed */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. */ + /* (Because of the length of the timeout, */ + /* this transition will cause the token to be */ + /* passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + MSTP_Flag.ReceivedInvalidFrame = false; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedValidFrame == true) { + if (DestinationAddress == This_Station) { + /* What did we receive? */ + switch (FrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates + a reply */ + /* indicate successful reception to + the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens */ + /* or a device that didn't see activity after passing */ + /* a token (how lame!). */ + /* Synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + } + MSTP_Flag.ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (FrameCount < Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else if ((MSTP_Flag.SoleMaster == false) && + (Next_Station == This_Station)) { + /* NextStationUnknown - added in Addendum 135-2008v-1 */ + /* then the next station to which the token + should be sent is unknown - so PollForMaster */ + Poll_Station = next_this_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (TokenCount < (Npoll - 1)) { + if ((MSTP_Flag.SoleMaster == true) && + (Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent + (true master-slave operation). */ + FrameCount = 0; + TokenCount++; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 + eliminates the Poll For Master + if there are no addresses between + TS and NS, since there is no + address at which a new master node + may be found in that case. */ + TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == Next_Station) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + Poll_Station = next_next_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + Poll_Station = This_Station; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + Poll_Station = next_poll_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (rs485_silence_elapsed(Tusage_timeout)) { + if (RetryCount < Nretry_token) { + /* RetrySendToken */ + RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + /* note: if NS=TS-1, this node could send PFM to self! */ + Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } else { + if (EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by + the new token user. */ + /* Enter the IDLE state to process the frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } + break; + /* The NO_TOKEN state is entered if Silence Timer + becomes greater than Tno_token, indicating that + there has been no network activity for that period + of time. The timeout is continued to determine + whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * This_Station); + if (rs485_silence_elapsed(my_timeout)) { + ns_timeout = Tno_token + (Tslot * (This_Station + 1)); + if (rs485_silence_elapsed(ns_timeout)) { + /* should never get here unless timer resolution is bad */ + rs485_silence_reset(); + Master_State = MSTP_MASTER_STATE_IDLE; + } else { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* indicate that the next station is unknown */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state + to find a new successor to TS. */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } else { + if (EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and + process the incoming frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (MSTP_Flag.ReceivedValidFrame == true) { + if ((DestinationAddress == This_Station) + && (FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + MSTP_Flag.SoleMaster = false; + Next_Station = SourceAddress; + EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + Poll_Station = This_Station; + TokenCount = 0; + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + MSTP_Flag.ReceivedValidFrame = false; + } else if ((rs485_silence_elapsed(Tusage_timeout)) || + (MSTP_Flag.ReceivedInvalidFrame == true)) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + FrameCount = 0; + /* TokenCount++; removed in 2004 */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (Next_Station != This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != This_Station) { + /* SendNextPFM */ + Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, + Poll_Station, This_Station, NULL, 0); + RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station + is the only master */ + MSTP_Flag.SoleMaster = true; + FrameCount = 0; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + MSTP_Flag.ReceivedInvalidFrame = false; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + if (rs485_silence_elapsed(Treply_delay)) { + Master_State = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + } else { + pkt = (struct mstp_pdu_packet *) Ringbuf_Peek(&PDU_Queue); + if (pkt != NULL) { + matched = + dlmstp_compare_data_expecting_reply(&InputBuffer[0], + DataLength, SourceAddress, &pkt->buffer[0], + pkt->length, pkt->destination_mac); + } else { + matched = false; + } + if (matched) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + uint8_t frame_type; + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = + FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, + This_Station, (uint8_t *) & pkt->buffer[0], + pkt->length); + Master_State = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + /* clear the queue */ + (void) Ringbuf_Pop(&PDU_Queue, NULL); + } else if (pkt != NULL) { + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress, + This_Station, NULL, 0); + Master_State = MSTP_MASTER_STATE_IDLE; + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + } + } + break; + default: + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +/* returns true if the Send PDU Queue is Empty */ +bool dlmstp_send_pdu_queue_empty(void) +{ + return Ringbuf_Empty(&PDU_Queue); +} + +/* returns true if the Send PDU Queue is Full */ +bool dlmstp_send_pdu_queue_full(void) +{ + return Ringbuf_Full(&PDU_Queue); +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu(BACNET_ADDRESS * dest, /* destination address */ + + BACNET_NPDU_DATA * npdu_data, /* network information */ + + uint8_t * pdu, /* any data to be sent - may be null */ + + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + struct mstp_pdu_packet *pkt; + uint16_t i = 0; + + pkt = (struct mstp_pdu_packet *) Ringbuf_Alloc(&PDU_Queue); + if (pkt) { + pkt->data_expecting_reply = npdu_data->data_expecting_reply; + for (i = 0; i < pdu_len; i++) { + pkt->buffer[i] = pdu[i]; + } + pkt->length = pdu_len; + if (dest->mac_len == 0) { + pkt->destination_mac = MSTP_BROADCAST_ADDRESS; + } else { + pkt->destination_mac = dest->mac[0]; + } + bytes_sent = pdu_len; + } + + return bytes_sent; +} +/** + * Handle the running of the MS/TP state machine by receiving bytes + * and placing them into packets. If a packet is received, the number of + * bytes returned is non-zero. + * + * @param src [out] - the source address of the packet received + * @param pdu [out] - the PDU buffer which is filled with the packet. + * @param max_pdu [in] - the size of the PDU buffer + * @param timeout [in] - the number of milliseconds to wait for a packet + * + * @return Return the length of the packet + */ +uint16_t dlmstp_receive(BACNET_ADDRESS * src, + uint8_t * pdu, + uint16_t max_pdu, + unsigned timeout) +{ + uint16_t pdu_len = 0; /* return value */ + bool transmitting = false; + + (void)timeout; + /* set the input buffer to the same data storage for zero copy */ + if (!InputBuffer) { + InputBuffer = pdu; + InputBufferSize = max_pdu; + } + transmitting = rs485_rts_enabled(); + if (!transmitting) { + while ((MSTP_Flag.ReceivedValidFrame == false) && + (MSTP_Flag.ReceivedValidFrameNotForUs == false) && + (MSTP_Flag.ReceivedInvalidFrame == false)) { + /* only do receive state machine while we don't have a frame */ + MSTP_Receive_Frame_FSM(); + /* process another byte, if available, since this handler + may not get called often enough to process fast baud long + messages. */ + if (!rs485_byte_available(NULL)) { + break; + } + } + } + /* only do master state machine while rx is idle */ + if ((Receive_State == MSTP_RECEIVE_STATE_IDLE) && (transmitting == false)) { + if (MSTP_Flag.ReceivedValidFrameNotForUs) { + MSTP_Flag.ReceivedValidFrameNotForUs = false; + } else if (MSTP_Flag.ReceivedValidFrame) { + if (rs485_turnaround_elapsed()) { + if ((This_Station > 127) && (This_Station < 255)) { + MSTP_Slave_Node_FSM(); + } else if (This_Station <= 127) { + while (MSTP_Master_Node_FSM()) { + /* do nothing while some states fast transition */ + }; + } + } + } else { + if ((This_Station > 127) && (This_Station < 255)) { + MSTP_Slave_Node_FSM(); + } else if (This_Station <= 127) { + while (MSTP_Master_Node_FSM()) { + /* do nothing while some states fast transition */ + }; + } + } + } + /* if there is a packet that needs processed, do it now. */ + if (MSTP_Flag.ReceivePacketPending) { + MSTP_Flag.ReceivePacketPending = false; + pdu_len = DataLength; + src->mac_len = 1; + src->mac[0] = SourceAddress; + /* data is already in the pdu pointer */ + } + + return pdu_len; +} + +void dlmstp_set_mac_address(uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + /* Slave Nodes can only have address 0-254 */ + if (mac_address < 255) { + This_Station = mac_address; + } + + return; +} + +uint8_t dlmstp_mac_address(void) +{ + return This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames(uint8_t max_info_frames) +{ + if (max_info_frames >= MSTP_PDU_PACKET_COUNT) { + Nmax_info_frames = max_info_frames; + } + + return; +} + +uint8_t dlmstp_max_info_frames(void) +{ + return Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master(uint8_t max_master) +{ + if (max_master <= 127) { + Nmax_master = max_master; + } + + return; +} + +uint8_t dlmstp_max_master(void) +{ + return Nmax_master; +} + +void dlmstp_get_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address(BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +bool dlmstp_sole_master(void) +{ + if (MSTP_Flag.SoleMaster) { + return true; + } + + return false; +} diff --git a/bacnet-stack/ports/xplained/led.c b/bacnet-stack/ports/xplained/led.c new file mode 100644 index 00000000..8062f1a2 --- /dev/null +++ b/bacnet-stack/ports/xplained/led.c @@ -0,0 +1,199 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include "board.h" +#include "ioport.h" +#include "timer.h" +#include "led.h" + +#ifdef CONF_BOARD_ENABLE_RS485_XPLAINED +#define RS485_XPLAINED_LD1 IOPORT_CREATE_PIN(PORTC, 6) +#define RS485_XPLAINED_LD2 IOPORT_CREATE_PIN(PORTC, 7) +#define RS485_XPLAINED_LD3 IOPORT_CREATE_PIN(PORTC, 4) +#define RS485_XPLAINED_LD4 IOPORT_CREATE_PIN(PORTC, 5) + +static struct itimer Off_Delay_Timer[LEDS_MAX]; + +/************************************************************************* +* Description: Turn on an LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_on(uint8_t index) +{ + switch (index) { + case 0: + ioport_set_value(RS485_XPLAINED_LD1, 1); + break; + case 1: + ioport_set_value(RS485_XPLAINED_LD2, 1); + break; + case 2: + ioport_set_value(RS485_XPLAINED_LD3, 1); + break; + case 3: + ioport_set_value(RS485_XPLAINED_LD4, 1); + break; + default: + break; + } + if (index < LEDS_MAX) { + timer_interval_infinity(&Off_Delay_Timer[index]); + } +} + +/************************************************************************* +* Description: Turn off an LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_off(uint8_t index) +{ + switch (index) { + case 0: + ioport_set_value(RS485_XPLAINED_LD1, 0); + break; + case 1: + ioport_set_value(RS485_XPLAINED_LD2, 0); + break; + case 2: + ioport_set_value(RS485_XPLAINED_LD3, 0); + break; + case 3: + ioport_set_value(RS485_XPLAINED_LD4, 0); + break; + default: + break; + } + if (index < LEDS_MAX) { + timer_interval_infinity(&Off_Delay_Timer[index]); + } +} + +/************************************************************************* +* Description: Get the state of the LED +* Returns: true if on, false if off. +* Notes: none +*************************************************************************/ +bool led_state(uint8_t index) +{ + switch (index) { + case 0: + return ioport_pin_is_high(RS485_XPLAINED_LD1); + case 1: + return ioport_pin_is_high(RS485_XPLAINED_LD2); + case 2: + return ioport_pin_is_high(RS485_XPLAINED_LD3); + case 3: + return ioport_pin_is_high(RS485_XPLAINED_LD4); + default: + break; + } + + return false; +} + +/************************************************************************* +* Description: Toggle the state of the setup LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_toggle(uint8_t index) +{ + if (led_state(index)) { + led_off(index); + } else { + led_on(index); + } +} + +/************************************************************************* +* Description: Delay before going off to give minimum brightness. +* Returns: none +* Notes: none +*************************************************************************/ +void led_off_delay(uint8_t index, + uint32_t delay_ms) +{ + if (index < LEDS_MAX) { + timer_interval_start(&Off_Delay_Timer[index], delay_ms); + } +} + +/************************************************************************* +* Description: Turn on, and delay before going off. +* Returns: none +* Notes: none +*************************************************************************/ +void led_on_interval(uint8_t index, + uint16_t interval_ms) +{ + if (index < LEDS_MAX) { + led_on(index); + timer_interval_start(&Off_Delay_Timer[index], interval_ms); + } +} + +/************************************************************************* +* Description: Task for blinking LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_task(void) +{ + uint8_t i; /* loop counter */ + + for (i = 0; i < LEDS_MAX; i++) { + if (timer_interval_expired(&Off_Delay_Timer[i])) { + timer_interval_infinity(&Off_Delay_Timer[i]); + led_off(i); + } + } +} + +/************************************************************************* +* Description: Initialize the LED hardware +* Returns: none +* Notes: none +*************************************************************************/ +void led_init(void) +{ + uint8_t i; /* loop counter */ + + /* configure the LEDs for Rx and Tx indication */ + ioport_configure_pin(RS485_XPLAINED_LD1, + IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW); + ioport_configure_pin(RS485_XPLAINED_LD2, + IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW); + ioport_configure_pin(RS485_XPLAINED_LD3, + IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW); + ioport_configure_pin(RS485_XPLAINED_LD4, + IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW); + /* initialize the timers, while giving LEDs a brief test */ + for (i = 0; i < LEDS_MAX; i++) { + led_on(i); + led_off_delay(i,500); + } +} +#endif diff --git a/bacnet-stack/ports/xplained/led.h b/bacnet-stack/ports/xplained/led.h new file mode 100644 index 00000000..d77a8fa2 --- /dev/null +++ b/bacnet-stack/ports/xplained/led.h @@ -0,0 +1,76 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef LED_H +#define LED_H + +#include +#include + +#define LED_RS485_RX 0 +#define LED_RS485_TX 1 +#define LED_APDU 2 +#define LED_DEBUG 3 + +#define LEDS_MAX 4 + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef CONF_BOARD_ENABLE_RS485_XPLAINED + void led_on( + uint8_t index); + void led_on_interval( + uint8_t index, + uint16_t interval_ms); + void led_off( + uint8_t index); + void led_off_delay( + uint8_t index, + uint32_t delay_ms); + void led_toggle( + uint8_t index); + bool led_state( + uint8_t index); + void led_task( + void); + void led_init( + void); +#else + /* dummy stubs */ + #define led_on(x) + #define led_on_interval(x, ms) + #define led_off(x) + #define led_off_delay(x, ms) + #define led_toggle(x) + #define led_state(x) + #define led_task() + #define led_init() +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/xplained/main.c b/bacnet-stack/ports/xplained/main.c new file mode 100644 index 00000000..61f4d3d1 --- /dev/null +++ b/bacnet-stack/ports/xplained/main.c @@ -0,0 +1,49 @@ +/** + * \file + * + * \brief XMEGA-A3BU BACnet application + * + */ +#include +#include "timer.h" +#include "rs485.h" +#include "led.h" +#include "adc-hdw.h" +#include "dlmstp.h" +#include "bacnet.h" + +/** + * \brief Main function. + * + * Initializes the board, and runs the application in an infinite loop. + */ +int main(void) +{ + /* hardware initialization */ + sysclk_init(); + board_init(); + pmic_init(); + timer_init(); + rs485_init(); + led_init(); + adc_init(); +#ifdef CONF_BOARD_ENABLE_RS485_XPLAINED + // Enable display backlight + gpio_set_pin_high(NHD_C12832A1Z_BACKLIGHT); +#endif + // Workaround for known issue: Enable RTC32 sysclk + sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_RTC); + while (RTC32.SYNCCTRL & RTC32_SYNCBUSY_bm) { + // Wait for RTC32 sysclk to become stable + } + cpu_irq_enable(); + /* application initialization */ + rs485_baud_rate_set(38400); + bacnet_init(); + /* run forever - timed tasks */ + timer_callback(bacnet_task_timed, 5); + for (;;) { + bacnet_task(); + led_task(); + } +} diff --git a/bacnet-stack/ports/xplained/nvmdata.c b/bacnet-stack/ports/xplained/nvmdata.c new file mode 100644 index 00000000..91197791 --- /dev/null +++ b/bacnet-stack/ports/xplained/nvmdata.c @@ -0,0 +1,41 @@ +/** +* @file +* @author Steve Karg +* @date 2013 +* @brief Store and retrieve non-volatile data +* +*/ +#include +#include +#include "nvmdata.h" +#include "dlmstp.h" +#include "device.h" + +/** +* Initializes the non-volatile memory module +*/ +void nvm_data_init(void) +{ + uint32_t device_id = 127; + uint8_t max_master = 127; + uint8_t mac_address = 127; + + nvm_read(NVM_MAC_ADDRESS, &mac_address, 1); + if (mac_address == 255) { + /* uninitialized */ + mac_address = 123; + } + dlmstp_set_mac_address(mac_address); + nvm_read(NVM_MAX_MASTER, &max_master, 1); + if (max_master > 127) { + max_master = 127; + } + dlmstp_set_max_master(max_master); + /* Get the device ID from the EEPROM */ + nvm_read(NVM_DEVICE_0, (uint8_t *) & device_id, sizeof(device_id)); + if (device_id < BACNET_MAX_INSTANCE) { + Device_Set_Object_Instance_Number(device_id); + } else { + Device_Set_Object_Instance_Number(mac_address); + } +} diff --git a/bacnet-stack/ports/xplained/nvmdata.h b/bacnet-stack/ports/xplained/nvmdata.h new file mode 100644 index 00000000..6990ad8e --- /dev/null +++ b/bacnet-stack/ports/xplained/nvmdata.h @@ -0,0 +1,84 @@ +/************************************************************************ +* +* Copyright (C) 2013 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*************************************************************************/ +#ifndef NVM_DATA_H +#define NVM_DATA_H + +#include + +/* compatible functions could put in nvm.h to abstract more */ +#define nvm_write(dst, src, len) \ + eeprom_write_block((uint8_t *)(src),(uint8_t *)(dst), (size_t)(len)) + +#define nvm_read(src, dst, len) \ + eeprom_read_block((uint8_t *)dst, (const uint8_t *)(src),(size_t)(len)) + +/*=============== EEPROM ================*/ +/* define EEPROM signature version */ +#define NVM_SIGNATURE 0 +#define NVM_VERSION 1 + +/* define the MAC, BAUD, MAX Master, Device Instance internal + so that bootloader *could* use them. */ +/* note: MAC could come from DIP switch, or be in non-volatile memory */ +#define NVM_MAC_ADDRESS 2 +/* 9=9.6k, 19=19.2k, 38=38.4k, 57=57.6k, 76=76.8k, 115=115.2k */ +#define NVM_BAUD_K 3 +#define NVM_MAX_MASTER 4 +/* device instance is only 22 bits - easier if we use 32 bits */ +#define NVM_DEVICE_0 5 +#define NVM_DEVICE_1 6 +#define NVM_DEVICE_2 7 +#define NVM_DEVICE_3 8 + +/* free space - 9..31 */ + +/* BACnet Names - 32 bytes of data each */ +#define NVM_NAME_LENGTH(n) ((n)+0) +#define NVM_NAME_ENCODING(n) ((n)+1) +#define NVM_NAME_STRING(n) ((n)+2) +#define NVM_NAME_SIZE 30 +#define NVM_NAME_OFFSET (1+1+NVM_NAME_SIZE) +/* Device Name - starting offset */ +#define NVM_DEVICE_NAME 32 +/* Device Description - starting offset */ +#define NVM_DEVICE_DESCRIPTION \ + (NVM_DEVICE_NAME+NVM_NAME_OFFSET) +/* Device Location - starting offset */ +#define NVM_DEVICE_LOCATION \ + (NVM_DEVICE_DESCRIPTION+NVM_NAME_OFFSET) + +/* free space 128..4096 */ + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +void nvm_data_init(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/xplained/rs485.c b/bacnet-stack/ports/xplained/rs485.c new file mode 100644 index 00000000..f9bec068 --- /dev/null +++ b/bacnet-stack/ports/xplained/rs485.c @@ -0,0 +1,343 @@ +/* + * @file + * + * @brief RS-485 Interface + * + * Copyright (C) 2013 Steve Karg + * + * @page License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include +#include +#include "board.h" +#include "usart.h" +#include "ioport.h" +#include "sysclk.h" +#include "fifo.h" +#include "timer.h" +#include "led.h" +#include "mstpdef.h" +/* me! */ +#include "rs485.h" + +#ifdef CONF_BOARD_ENABLE_RS485_XPLAINED +#define RS485_RE IOPORT_CREATE_PIN(PORTC, 1) +#define RS485_DE IOPORT_CREATE_PIN(PORTC, 0) +#define RS485_TXD IOPORT_CREATE_PIN(PORTC, 3) +#define RS485_RXD IOPORT_CREATE_PIN(PORTC, 2) +#define RS485_USART USARTC0 +#define RS485_TXC_vect USARTC0_TXC_vect +#define RS485_RXC_vect USARTC0_RXC_vect +#else +#define RS485_RE IOPORT_CREATE_PIN(PORTE, 0) +#define RS485_DE IOPORT_CREATE_PIN(PORTE, 0) +#define RS485_TXD IOPORT_CREATE_PIN(PORTE, 3) +#define RS485_RXD IOPORT_CREATE_PIN(PORTE, 2) +#define RS485_USART USARTE0 +#define RS485_TXC_vect USARTE0_TXC_vect +#define RS485_RXC_vect USARTE0_RXC_vect +#endif + +/* buffer for storing received bytes - size must be power of two */ +/* BACnet MAX_APDU for MS/TP is 480 bytes */ +static uint8_t Receive_Queue_Data[512]; +static FIFO_BUFFER Receive_Queue; +/* buffer for storing bytes to transmit - size must be power of two */ +/* BACnet MAX_APDU for MS/TP is 480 bytes */ +static uint8_t Transmit_Queue_Data[512]; +static FIFO_BUFFER Transmit_Queue; +/* baud rate of the UART interface */ +static uint32_t Baud_Rate; +/* timer for measuring line silence */ +static struct etimer Silence_Timer; +/* flag to track RTS status */ +static volatile bool RTS_Status; + +/** + * Resets the silence timer + */ +void rs485_silence_reset(void) +{ + timer_elapsed_start(&Silence_Timer); +} + +/** + * Determine the amount of silence on the wire from the timer. + * + * @param interval - amount of time in milliseconds that line could be silent + * + * @return true if the line has been silent for the interval + */ +bool rs485_silence_elapsed(uint32_t interval) +{ + return timer_elapsed_milliseconds(&Silence_Timer, interval); +} + +/** + * enable the transmit-enable line on the RS-485 transceiver + * + * @param enable - true to enable RTS, false to disable RTS + */ +void rs485_rts_enable(bool enable) +{ + if (enable) { + /* Turn Tx enable on */ + ioport_set_value(RS485_RE, 1); + ioport_set_value(RS485_DE, 1); + led_on(LED_RS485_TX); + RTS_Status = true; + } else { + /* Turn Tx enable off */ + ioport_set_value(RS485_RE, 0); + ioport_set_value(RS485_DE, 0); + led_off_delay(LED_RS485_TX, 10); + RTS_Status = false; + } +} + +/** + * Determine the status of the transmit-enable line on the RS-485 transceiver + * + * @return true if RTS is enabled, false if RTS is disabled + */ +bool rs485_rts_enabled(void) +{ + return RTS_Status; +} + +/** + * Baud rate determines turnaround time. + * The minimum time after the end of the stop bit of the final octet of a + * received frame before a node may enable its EIA-485 driver: 40 bit times. + * At 9600 baud, 40 bit times would be about 4.166 milliseconds + * At 19200 baud, 40 bit times would be about 2.083 milliseconds + * At 38400 baud, 40 bit times would be about 1.041 milliseconds + * At 57600 baud, 40 bit times would be about 0.694 milliseconds + * At 76800 baud, 40 bit times would be about 0.520 milliseconds + * At 115200 baud, 40 bit times would be about 0.347 milliseconds + * 40 bits is 4 octets including a start and stop bit with each octet + * + * @return: amount of milliseconds to wait + */ +static uint16_t rs485_turnaround_time(void) +{ + /* delay after reception before transmitting - per MS/TP spec */ + /* wait a minimum 40 bit times since reception */ + /* at least 2 ms for errors: rounding, clock tick */ + return (2 + ((Tturnaround * 1000) / Baud_Rate)); +} + +/** + * Use the silence timer to determine turnaround time. + * + * @return true if the line has been silent for the turnaround interval + */ +bool rs485_turnaround_elapsed(void) +{ + return timer_elapsed_milliseconds(&Silence_Timer, rs485_turnaround_time()); +} + +/** + * Checks for data on the receive UART, and handles errors + * + * @param data register to store the byte, if available (can be NULL) + * + * @return true if a byte is available + */ +bool rs485_byte_available(uint8_t * data_register) +{ + bool data_available = false; /* return value */ + + if (FIFO_Empty(&Receive_Queue)) { + led_off_delay(LED_RS485_RX, 2); + } else { + led_on(LED_RS485_RX); + if (data_register) { + *data_register = FIFO_Get(&Receive_Queue); + } + data_available = true; + } + + return data_available; +} + +/** + * returns an error indication if errors are enabled + * + * @return returns true if error is detected and errors are enabled + */ +bool rs485_receive_error(void) +{ + return false; +} + +/** + * Determines if the entire frame is sent from USART FIFO + * + * @return true if the USART FIFO is empty + */ +bool rs485_frame_sent(void) +{ + return usart_tx_is_complete(&RS485_USART); +} + +/** +* Transmit one or more bytes on RS-485. Can be called while transmitting to add +* additional bytes to transmit queue. +* +* @param buffer - array of one or more bytes to transmit +* @param nbytes - number of bytes to transmit +*/ +bool rs485_bytes_send(uint8_t * buffer, + uint16_t nbytes) +{ + bool status = false; + bool start_required = false; + uint8_t ch = 0; + + if (buffer && (nbytes > 0)) { + if (FIFO_Empty(&Transmit_Queue)) { + start_required = true; + } + status = FIFO_Add(&Transmit_Queue, buffer, nbytes); + if (start_required && status) { + rs485_rts_enable(true); + timer_elapsed_start(&Silence_Timer); + ch = FIFO_Get(&Transmit_Queue); + usart_clear_tx_complete(&RS485_USART); + usart_set_tx_interrupt_level(&RS485_USART, USART_INT_LVL_LO); + usart_putchar(&RS485_USART, ch); + } + } + + return status; +} + +/** +* RS485 RX interrupt +*/ +ISR(RS485_RXC_vect) +{ + unsigned char ch; + + ch = usart_getchar(&RS485_USART); + FIFO_Put(&Receive_Queue, ch); + usart_clear_rx_complete(&RS485_USART); +} + +/** +* RS485 TX interrupt +*/ +ISR(RS485_TXC_vect) +{ + uint8_t ch; + + if (FIFO_Empty(&Transmit_Queue)) { + /* end of packet */ + usart_set_tx_interrupt_level(&RS485_USART, USART_INT_LVL_OFF); + rs485_rts_enable(false); + } else { + rs485_rts_enable(true); + ch = FIFO_Get(&Transmit_Queue); + usart_putchar(&RS485_USART, ch); + } +} + +/** + * Return the RS-485 baud rate + * + * @return baud - RS-485 baud rate in bits per second (bps) + */ +uint32_t rs485_baud_rate( + void) +{ + return Baud_Rate; +} + +/** + * Initialize the RS-485 baud rate + * + * @param baud - RS-485 baud rate in bits per second (bps) + * + * @return true if set and valid + */ +bool rs485_baud_rate_set( + uint32_t baud) +{ + bool valid = true; + unsigned long frequency; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + frequency = sysclk_get_peripheral_bus_hz(&RS485_USART); + valid = usart_set_baudrate (&RS485_USART, baud, frequency); + if (valid) { + Baud_Rate = baud; + } + break; + default: + valid = false; + break; + } + + return valid; +} + + +/** + * Initialize the RS-485 UART interface, receive interrupts enabled + */ +void rs485_init(void) +{ + usart_rs232_options_t option; + + /* initialize the Rx and Tx byte queues */ + FIFO_Init(&Receive_Queue, &Receive_Queue_Data[0], + (unsigned) sizeof(Receive_Queue_Data)); + FIFO_Init(&Transmit_Queue, &Transmit_Queue_Data[0], + (unsigned) sizeof(Transmit_Queue_Data)); + /* initialize the silence timer */ + timer_elapsed_start(&Silence_Timer); + /* configure the TX pin */ + ioport_configure_pin(RS485_TXD, + IOPORT_DIR_OUTPUT | IOPORT_INIT_HIGH); + /* configure the RX pin */ + ioport_configure_pin(RS485_RXD, + IOPORT_DIR_INPUT); + /* configure the RTS pins */ + ioport_configure_pin(RS485_RE, + IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW); + ioport_configure_pin(RS485_DE, + IOPORT_DIR_OUTPUT | IOPORT_INIT_LOW); + option.baudrate = Baud_Rate; + option.charlength = USART_CHSIZE_8BIT_gc; + option.paritytype = USART_PMODE_DISABLED_gc; + option.stopbits = false; + usart_init_rs232(&RS485_USART, &option); + usart_set_rx_interrupt_level(&RS485_USART, USART_INT_LVL_HI); +} diff --git a/bacnet-stack/ports/xplained/rs485.h b/bacnet-stack/ports/xplained/rs485.h new file mode 100644 index 00000000..15fcfc9f --- /dev/null +++ b/bacnet-stack/ports/xplained/rs485.h @@ -0,0 +1,63 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef RS485_H +#define RS485_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + void rs485_rts_enable( + bool enable); + bool rs485_rts_enabled( + void); + bool rs485_byte_available( + uint8_t * data_register); + bool rs485_receive_error( + void); + bool rs485_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes); /* number of bytes of data */ + bool rs485_frame_sent(void); + bool rs485_turnaround_elapsed( + void); + + uint32_t rs485_baud_rate( + void); + bool rs485_baud_rate_set( + uint32_t baud); + + void rs485_silence_reset( + void); + bool rs485_silence_elapsed( + uint32_t interval); + + void rs485_init(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/xplained/stack.c b/bacnet-stack/ports/xplained/stack.c new file mode 100644 index 00000000..13ba6c17 --- /dev/null +++ b/bacnet-stack/ports/xplained/stack.c @@ -0,0 +1,144 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include "board.h" +/* me */ +#include "stack.h" + +#if defined(__GNUC__) +/* stack checking technique by Michael McTernan */ +/* With AVR gcc, two symbols are defined by the linker + that can make this easy. These are _end and __stack + which define the first free byte of SRAM after + program variables, and the starting address of + the stack, respectively. + + The stack starts at __stack, which is conventionally + the highest byte of SRAM, and grows towards zero; + _end will be somewhere between zero and __stack. + If the stack ever falls below _end, it has almost + certainly corrupted program data. + + The following C declarations gain access to these + linker symbols: +*/ +extern uint8_t _end; +extern uint8_t __stack; + +/* canary value */ +#define STACK_CANARY (0xC5) + +/* This is declared in such a way that AVR-libc will + execute the assembly before the program has started + running or configured the stack. It also runs at a + point before some of the normal runtime setup, + hence assembly should be used as C may not be + fully reliable (this is discussed in the AVR libc manual). +*/ +void stack_init( + void) __attribute__ ((naked)) __attribute__ ((section(".init1"))); + +/* The function itself simply fills the stack with the canary value, + the idea being that stack usage will overwrite this with some + other value, hence making stack usage detectable. +*/ +void stack_init( + void) +{ +#if 0 + uint8_t *p = &_end; + + while (p <= &__stack) { + *p = STACK_CANARY; + p++; + } +#else + __asm volatile ( + " ldi r30,lo8(_end)\n" " ldi r31,hi8(_end)\n" " ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */ + " ldi r25,hi8(__stack)\n" " rjmp .cmp\n" ".loop:\n" + " st Z+,r24\n" ".cmp:\n" " cpi r30,lo8(__stack)\n" + " cpc r31,r25\n" " brlo .loop\n" " breq .loop"::); +#endif +} + +unsigned stack_size( + void) +{ + return (&__stack) - (&_end); +} + +uint8_t stack_byte( + unsigned offset) +{ + return *(&_end + offset); +} + +/* The following function can be used to count + how many bytes of stack have not been overwritten. + This function can be called at any time + to check how much stack space has never been over written. + If it returns 0, you are probably in trouble as + all the stack has been used, most likely destroying + some program variables. +*/ +unsigned stack_unused( + void) +{ + uint8_t *p = &_end; + unsigned count = 0; + + while (p <= &__stack) { + if ((*p) != STACK_CANARY) { + count = p - (&_end); + break; + } + p++; + } + return count; +} +#else +void stack_init( + void) +{ + +} + +unsigned stack_size( + void) +{ + return 0; +} + +uint8_t stack_byte( + unsigned offset) +{ + return 0; +} + +unsigned stack_unused( + void) +{ + return 0; +} +#endif diff --git a/bacnet-stack/ports/xplained/stack.h b/bacnet-stack/ports/xplained/stack.h new file mode 100644 index 00000000..715a8536 --- /dev/null +++ b/bacnet-stack/ports/xplained/stack.h @@ -0,0 +1,51 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef STACK_H +#define STACK_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* C stack checking */ + void stack_init( + void); + + unsigned stack_size( + void); + + uint8_t stack_byte( + unsigned offset); + + unsigned stack_unused( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/xplained/timer.c b/bacnet-stack/ports/xplained/timer.c new file mode 100644 index 00000000..c5ff19e9 --- /dev/null +++ b/bacnet-stack/ports/xplained/timer.c @@ -0,0 +1,338 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include "timer.h" + +/* interval and elapsed millisecond timer */ +/* interval not to exceed 49.7 days */ +/* minimum interval of 1ms may be 0 to 1ms */ + +/************************************************************************* +* Description: Start a timer from now. +* Returns: elapsed milliseconds since last set +* Notes: none +**************************************************************************/ +uint32_t timer_interval_start(struct itimer *t, uint32_t interval) +{ + uint32_t now = 0; + uint32_t elapsed = 0; + + now = timer_milliseconds(); + elapsed = now - t->start; + t->start = now; + t->interval = interval; + + return elapsed; +} + +/************************************************************************* +* Description: Start a timer from now. +* Returns: elapsed seconds since last set +* Notes: none +**************************************************************************/ +uint32_t timer_interval_start_seconds(struct itimer *t, uint32_t interval) +{ + uint32_t elapsed = 0; + + interval *= 1000L; + elapsed = timer_interval_start(t, interval); + elapsed /= 1000L; + + return elapsed; +} + +/************************************************************************* +* Description: Start a timer from now. +* Returns: elapsed minutes since last set +* Notes: none +**************************************************************************/ +uint32_t timer_interval_start_minutes(struct itimer *t, uint32_t interval) +{ + uint32_t elapsed = 0; + + interval *= 60L; + interval *= 1000L; + + elapsed = timer_interval_start(t, interval); + elapsed /= 1000L; + elapsed /= 60L; + + return elapsed; +} + +/************************************************************************* +* Description: Change the timer interval without restart +* Returns: previous interval value +* Notes: none +**************************************************************************/ +uint32_t timer_interval_adjust(struct itimer *t, uint32_t interval) +{ + uint32_t previous_interval = t->interval; + + t->interval = interval; + + return previous_interval; +} + +/************************************************************************* +* Description: Reset the timer with the same interval +* Returns: none +* Notes: none +**************************************************************************/ +void timer_interval_reset(struct itimer *t) +{ + t->start += t->interval; +} + +/************************************************************************* +* Description: Restart the timer from now, syncing on existing interval +* Returns: elapsed milliseconds since last reset +* Notes: none +**************************************************************************/ +uint32_t timer_interval_resync(struct itimer *t) +{ + uint32_t elapsed; + uint32_t intervals; + uint32_t gap; + uint32_t now; + + now = timer_milliseconds(); + elapsed = now - t->start; + if ((t->interval != TIMER_INTERVAL_MAX) && (t->interval != 0)) { + if (elapsed >= t->interval) { + /* catch up to now */ + intervals = elapsed / t->interval; + gap = intervals * t->interval; + t->start += gap; + } + } + + return elapsed; +} + +/************************************************************************* +* Description: Restart the timer from now +* Returns: elapsed milliseconds since last set +* Notes: none +**************************************************************************/ +uint32_t timer_interval_restart(struct itimer *t) +{ + uint32_t now; + uint32_t elapsed; + + now = timer_milliseconds(); + elapsed = now - t->start; + t->start = now; + + return elapsed; +} + +/************************************************************************* +* Description: Reset the timer with the zero interval - always expired +* Returns: none +* Notes: none +**************************************************************************/ +void timer_interval_none(struct itimer *t) +{ + t->interval = 0; +} + +/************************************************************************* +* Description: Reset the timer with the max interval - never expires +* Returns: none +* Notes: none +**************************************************************************/ +void timer_interval_infinity(struct itimer *t) +{ + t->interval = TIMER_INTERVAL_MAX; +} + +/************************************************************************* +* Description: Determines if the timer has an active interval +* Returns: true if active +* Notes: none +**************************************************************************/ +bool timer_interval_active(struct itimer *t) +{ + return ((t->interval != TIMER_INTERVAL_MAX) && (t->interval != 0)); +} + +/************************************************************************* +* Description: Check to see if the time interval has elapsed +* Returns: true if expired +* Notes: Setting the interval to max never expires, to zero always expires +**************************************************************************/ +bool timer_interval_expired(struct itimer *t) +{ + uint32_t elapsed = 0; + bool status = false; + uint32_t now; + + if (t->interval == 0) { + status = true; + } else if (t->interval == TIMER_INTERVAL_MAX) { + status = false; + } else { + now = timer_milliseconds(); + elapsed = now - t->start; + if (elapsed >= t->interval) { + status = true; + } + } + + return status; +} + +/************************************************************************* +* Description: Return the elapsed time +* Returns: number of milliseconds elapsed +* Notes: none +**************************************************************************/ +uint32_t timer_interval_elapsed(struct itimer *t) +{ + uint32_t now; + uint32_t elapsed; + + now = timer_milliseconds(); + elapsed = now - t->start; + + return elapsed; +} + +/************************************************************************* +* Description: Return the interval time +* Returns: number of milliseconds for which the interval is set +* Notes: none +**************************************************************************/ +uint32_t timer_interval(struct itimer *t) +{ + return t->interval; +} + +/************************************************************************* +* Description: Return the interval time +* Returns: number of seconds for which the interval is set +* Notes: none +**************************************************************************/ +uint32_t timer_interval_seconds(struct itimer *t) +{ + return (t->interval/1000); +} + +/* Elapsed Timer */ + +/************************************************************************* +* Description: Restart the timer from now +* Returns: elapsed milliseconds since last set +* Notes: none +**************************************************************************/ +uint32_t timer_elapsed_start_offset(struct etimer *t, uint32_t offset) +{ + uint32_t now; + uint32_t elapsed; + + now = timer_milliseconds(); + elapsed = now - t->start; + if (offset) { + t->start = now + offset; + } else { + t->start = now; + } + + return elapsed; +} + +/************************************************************************* +* Description: Start a timer from now. +* Returns: elapsed milliseconds since last set +* Notes: none +**************************************************************************/ +uint32_t timer_elapsed_start(struct etimer *t) +{ + return timer_elapsed_start_offset(t, 0); +} + +/************************************************************************* +* Description: Return the elapsed time +* Returns: true if interval has elapsed +* Notes: none +**************************************************************************/ +bool timer_elapsed_milliseconds(struct etimer *t, uint32_t interval) +{ + bool status = false; + uint32_t delta; + + delta = timer_milliseconds() - t->start; + if (delta >= interval) { + status = true; + } + + return status; +} + +/************************************************************************* +* Description: Return the elapsed time +* Returns: true if interval has elapsed +* Notes: none +**************************************************************************/ +bool timer_elapsed_seconds(struct etimer *t, uint32_t interval) +{ + /* convert to seconds */ + interval *= 1000L; + + return timer_elapsed_milliseconds(t, interval); +} + +/************************************************************************* +* Description: Return the elapsed time +* Returns: true if interval has elapsed +* Notes: none +**************************************************************************/ +bool timer_elapsed_minutes(struct etimer *t, uint32_t interval) +{ + /* convert to seconds */ + interval *= 1000L; + /* convert to minutes */ + interval *= 60L; + + return timer_elapsed_milliseconds(t, interval); +} + +/************************************************************************* +* Description: Return the elapsed time +* Returns: number of milliseconds elapsed +* Notes: none +**************************************************************************/ +uint32_t timer_elapsed_time(struct etimer *t) +{ + uint32_t now; + uint32_t elapsed; + + now = timer_milliseconds(); + elapsed = now - t->start; + + return elapsed; +} + diff --git a/bacnet-stack/ports/xplained/timer.h b/bacnet-stack/ports/xplained/timer.h new file mode 100644 index 00000000..de852d38 --- /dev/null +++ b/bacnet-stack/ports/xplained/timer.h @@ -0,0 +1,96 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TIMER_H +#define TIMER_H + +#include +#include + +/* interval and elapsed millisecond timer */ +/* interval not to exceed 49.7 days */ +#define TIMER_INTERVAL_MAX UINT32_MAX + +/* structure for elapsed timer */ +struct etimer { + uint32_t start; +}; + +/* structure for interval timer */ +struct itimer { + uint32_t start; + uint32_t interval; +}; + +typedef void (*timer_callback_function) (void); + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* -- Interval Timer library -- */ + /* defined in generic timer module */ + uint32_t timer_interval_start(struct itimer *t, uint32_t interval); + uint32_t timer_interval_start_seconds(struct itimer *t, uint32_t interval); + uint32_t timer_interval_start_minutes(struct itimer *t, uint32_t interval); + /* adjust interval without restarting */ + uint32_t timer_interval_adjust(struct itimer *t, uint32_t interval); + /* adds interval to start - good for cyclic timers */ + void timer_interval_reset(struct itimer *t); + /* sets interval to zero - always expired */ + void timer_interval_none(struct itimer *t); + /* sets interval to max - never expires */ + void timer_interval_infinity(struct itimer *t); + /* syncs the start time to the next interval */ + uint32_t timer_interval_resync(struct itimer *t); + /* restarts the interval timer */ + uint32_t timer_interval_restart(struct itimer *t); + bool timer_interval_expired(struct itimer *t); + uint32_t timer_interval(struct itimer *t); + bool timer_interval_active(struct itimer *t); + uint32_t timer_interval_seconds(struct itimer *t); + uint32_t timer_interval_elapsed(struct itimer *t); + /* -- Elapsed Timer library - lower RAM usage or alternate functional usage -- */ + uint32_t timer_elapsed_start(struct etimer *t); + uint32_t timer_elapsed_start_offset(struct etimer *t, uint32_t offset); + bool timer_elapsed_milliseconds(struct etimer *t, uint32_t interval); + bool timer_elapsed_seconds(struct etimer *t, uint32_t interval); + bool timer_elapsed_minutes(struct etimer *t, uint32_t interval); + uint32_t timer_elapsed_time(struct etimer *t); + + /* define these functions in hardware specific timer module */ + void timer_init(void); + /* Raw API is used only by the elapsed and interval timer library. + Do not use it directly in your code. */ + uint32_t timer_milliseconds(void); + bool timer_callback( + timer_callback_function callback, + uint32_t milliseconds); + bool timer_callback_oneshot( + timer_callback_function callback, + uint32_t milliseconds); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/xplained/timer1.c b/bacnet-stack/ports/xplained/timer1.c new file mode 100644 index 00000000..879d9088 --- /dev/null +++ b/bacnet-stack/ports/xplained/timer1.c @@ -0,0 +1,180 @@ +/* + * @file + * + * @brief 1ms timer configuration + * + * Copyright (C) 2009 Steve Karg + * + * Created: 10/24/2013 8:58:56 PM + * Author: Steve + * + * @page License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include +#include +#include "tc.h" +#include "timer.h" + +/* define which timer counter we are using */ +#define MY_TIMER TCE0 + +/* number of callbacks supported */ +#ifndef TIMER_CALLBACK_MAX +#define TIMER_CALLBACK_MAX 8 +#endif + +/* counter for the base timer */ +static volatile uint32_t Millisecond_Counter; + +/* callback data structure */ +struct timer_data_t { + volatile uint32_t interval; + volatile uint32_t milliseconds; + timer_callback_function callback; +}; +static volatile struct timer_data_t Callback_Data[TIMER_CALLBACK_MAX]; + +/** + * Handles the interrupt from the timer + */ +static void my_callback(void) +{ + uint32_t now, t, interval, i; + + Millisecond_Counter++; + now = Millisecond_Counter; + for (i = 0; i < TIMER_CALLBACK_MAX; i++) { + /* check for callback */ + if (Callback_Data[i].callback) { + t = Callback_Data[i].milliseconds; + if (now >= t) { + Callback_Data[i].callback(); + interval = Callback_Data[i].interval; + if (interval) { + Callback_Data[i].milliseconds = now + interval; + } else { + /* disable any one-shot timers */ + Callback_Data[i].callback = NULL; + } + } + } + } +} + +/** + * Returns the continuous milliseconds count, which rolls over + * + * @return the current milliseconds count + */ +uint32_t timer_milliseconds(void) +{ + uint32_t timer_value; /* return value */ + + tc_set_overflow_interrupt_level(&MY_TIMER, TC_INT_LVL_OFF); + timer_value = Millisecond_Counter; + tc_set_overflow_interrupt_level(&MY_TIMER, TC_INT_LVL_LO); + + return timer_value; +} + +/** + * Configures and enables a repeating callback function + * + * @param callback - pointer to a #timer_callback_function function + * @param milliseconds - how often to call the function + * + * @return true if successfully added and enabled + */ +bool timer_callback( + timer_callback_function callback, + uint32_t milliseconds) +{ + bool status = false; + uint32_t now, i; + + tc_set_overflow_interrupt_level(&MY_TIMER, TC_INT_LVL_OFF); + now = Millisecond_Counter; + for (i = 0; i < TIMER_CALLBACK_MAX; i++) { + if (Callback_Data[i].callback == NULL) { + Callback_Data[i].interval = milliseconds; + /* set the first expiration time */ + Callback_Data[i].milliseconds = now + milliseconds; + Callback_Data[i].callback = callback; + status = true; + break; + } + } + tc_set_overflow_interrupt_level(&MY_TIMER, TC_INT_LVL_LO); + + return status; +} + +/** + * Configures and enables a one-shot callback function + * + * @param callback - pointer to a #timer_callback_function function + * @param milliseconds - how long to wait before calling the function + * + * @return true if successfully added and enabled + */ +bool timer_callback_oneshot( + timer_callback_function callback, + uint32_t milliseconds) +{ + bool status = false; + uint32_t now, i; + + tc_set_overflow_interrupt_level(&MY_TIMER, TC_INT_LVL_OFF); + now = Millisecond_Counter; + for (i = 0; i < TIMER_CALLBACK_MAX; i++) { + if (Callback_Data[i].callback == NULL) { + /* set the first expiration time */ + Callback_Data[i].milliseconds = now + milliseconds; + Callback_Data[i].interval = 0; + Callback_Data[i].callback = callback; + status = true; + break; + } + } + tc_set_overflow_interrupt_level(&MY_TIMER, TC_INT_LVL_LO); + + return status; +} + +/** + * Timer setup for 1 millisecond timer + */ +void timer_init(void) +{ + unsigned long period; + + tc_enable(&MY_TIMER); + tc_set_overflow_interrupt_callback(&MY_TIMER, my_callback); + tc_set_wgm(&MY_TIMER, TC_WG_NORMAL); + tc_write_count(&MY_TIMER, 1); + period = sysclk_get_peripheral_bus_hz(&MY_TIMER); + period /= 1000; + tc_write_period(&MY_TIMER, period); + tc_set_overflow_interrupt_level(&MY_TIMER, TC_INT_LVL_LO); + tc_write_clock_source(&MY_TIMER, TC_CLKSEL_DIV1_gc); +}