Compare commits
No commits in common. "master" and "pb-spi" have entirely different histories.
183 changed files with 940 additions and 11832 deletions
63
.github/workflows/githubci.yml
vendored
63
.github/workflows/githubci.yml
vendored
|
|
@ -1,63 +0,0 @@
|
|||
name: Build
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arduino-platform: ['metro_m0', 'hallowing', 'circuitplayground_m0',
|
||||
'metro_m4', 'pybadge_m4', 'pygamer_m4', 'hallowing_m4', 'pyportal_m4', 'pyportal_m4_titano']
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Checkout submodules
|
||||
shell: bash
|
||||
run: |
|
||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||
git submodule sync --recursive
|
||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive
|
||||
|
||||
- name: Install Arduino CLI and Tools
|
||||
run: |
|
||||
# make all our directories we need for files and libraries
|
||||
mkdir $HOME/.arduino15
|
||||
mkdir $HOME/.arduino15/packages
|
||||
mkdir $HOME/Arduino
|
||||
mkdir $HOME/Arduino/libraries
|
||||
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
|
||||
echo "::add-path::$GITHUB_WORKSPACE/bin"
|
||||
|
||||
- name: Install BSP and Libraries
|
||||
env:
|
||||
BSP_URL: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
|
||||
BSP_PATH: .arduino15/packages/adafruit/hardware/samd
|
||||
LIB_DEPS: FlashStorage SD
|
||||
run: |
|
||||
arduino-cli config init
|
||||
arduino-cli core update-index
|
||||
arduino-cli core update-index --additional-urls $BSP_URL
|
||||
arduino-cli core install arduino:samd --additional-urls $BSP_URL
|
||||
arduino-cli core install adafruit:samd --additional-urls $BSP_URL
|
||||
# Replace release BSP with our code
|
||||
BSP_VERSION=`eval ls $HOME/$BSP_PATH`
|
||||
rm -r $HOME/$BSP_PATH/*
|
||||
ln -s $GITHUB_WORKSPACE $HOME/$BSP_PATH/$BSP_VERSION
|
||||
arduino-cli lib install $LIB_DEPS
|
||||
|
||||
- name: Build examples
|
||||
run: python3 extras/build_all.py ${{ matrix.arduino-platform }}
|
||||
|
||||
# How to mark this as allowed-to-fail?
|
||||
- name: Build examples (-Wall)
|
||||
run: python3 extras/build_all.py --all_warnings --warnings_do_not_cause_job_failure
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "cores/arduino/TinyUSB/Adafruit_TinyUSB_ArduinoCore"]
|
||||
path = cores/arduino/TinyUSB/Adafruit_TinyUSB_ArduinoCore
|
||||
url = https://github.com/adafruit/Adafruit_TinyUSB_ArduinoCore.git
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
# Arduino Core for SAMD21 and SAMD51 CPU
|
||||
|
||||
[](https://github.com/adafruit/ArduinoCore-samd/actions)
|
||||
|
||||
This repository contains the source code and configuration files of the Arduino Core
|
||||
for Atmel's SAMD21 and SAMD51 processor (used on the Arduino/Genuino Zero, MKR1000 and MKRZero boards).
|
||||
|
||||
|
|
|
|||
1129
boards.txt
1129
boards.txt
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -218,11 +218,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -218,11 +218,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -218,11 +218,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -218,11 +218,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -222,11 +222,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -222,11 +222,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -222,11 +222,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -222,11 +222,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -226,11 +226,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -226,11 +226,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -226,11 +226,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -226,11 +226,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -218,11 +218,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -218,11 +218,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -218,11 +218,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -218,11 +218,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -222,11 +222,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -222,11 +222,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -222,11 +222,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -222,11 +222,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -226,11 +226,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -226,11 +226,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -226,11 +226,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -226,11 +226,7 @@ void I2S_Handler ( void );
|
|||
* \brief Configuration of the Cortex-M0+ Processor and Core Peripherals
|
||||
*/
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && (LITTLE_ENDIAN != 1)
|
||||
#error "Little Endian is already defined, but to different value than expected?!"
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1
|
||||
#endif
|
||||
#define LITTLE_ENDIAN 1
|
||||
#define __CM0PLUS_REV 1 /*!< Core revision r0p1 */
|
||||
#define __MPU_PRESENT 0 /*!< MPU present or not */
|
||||
#define __NVIC_PRIO_BITS 2 /*!< Number of bits used for Priority Levels */
|
||||
|
|
|
|||
|
|
@ -97,33 +97,8 @@ void loop( void ) ;
|
|||
#undef abs
|
||||
#endif // abs
|
||||
|
||||
#ifdef __cplusplus
|
||||
template<class T, class L>
|
||||
auto min(const T& a, const L& b) -> decltype((b < a) ? b : a)
|
||||
{
|
||||
return (b < a) ? b : a;
|
||||
}
|
||||
|
||||
template<class T, class L>
|
||||
auto max(const T& a, const L& b) -> decltype((b < a) ? b : a)
|
||||
{
|
||||
return (a < b) ? b : a;
|
||||
}
|
||||
#else
|
||||
#ifndef min
|
||||
#define min(a,b) \
|
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; })
|
||||
#endif
|
||||
#ifndef max
|
||||
#define max(a,b) \
|
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; })
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define min(a,b) ((a)<(b)?(a):(b))
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
#define abs(x) ((x)>0?(x):-(x))
|
||||
#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
|
||||
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
|
||||
|
|
@ -149,14 +124,10 @@ void loop( void ) ;
|
|||
#define digitalPinToInterrupt(P) ( P )
|
||||
#endif
|
||||
|
||||
// USB
|
||||
#ifdef USE_TINYUSB
|
||||
#include "Adafruit_TinyUSB_Core.h"
|
||||
#else
|
||||
// USB Device
|
||||
#include "USB/USBDesc.h"
|
||||
#include "USB/USBCore.h"
|
||||
#include "USB/USBAPI.h"
|
||||
#include "USB/USB_host.h"
|
||||
#endif
|
||||
|
||||
#endif // Arduino_h
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
// location from which to read.
|
||||
|
||||
#ifndef SERIAL_BUFFER_SIZE
|
||||
#define SERIAL_BUFFER_SIZE 350
|
||||
#define SERIAL_BUFFER_SIZE 256
|
||||
#endif
|
||||
|
||||
template <int N>
|
||||
|
|
|
|||
|
|
@ -537,18 +537,8 @@ bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag
|
|||
// 7-bits address + 1-bits R/W
|
||||
address = (address << 0x1ul) | flag;
|
||||
|
||||
// If another master owns the bus or the last bus owner has not properly
|
||||
// sent a stop, return failure early. This will prevent some misbehaved
|
||||
// devices from deadlocking here at the cost of the caller being responsible
|
||||
// for retrying the failed transmission. See SercomWireBusState for the
|
||||
// possible bus states.
|
||||
if(!isBusOwnerWIRE())
|
||||
{
|
||||
if( isBusBusyWIRE() || (isArbLostWIRE() && !isBusIdleWIRE()) || isBusUnknownWIRE() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Wait idle or owner bus mode
|
||||
while ( !isBusIdleWIRE() && !isBusOwnerWIRE() );
|
||||
|
||||
// Send start and address
|
||||
sercom->I2CM.ADDR.bit.ADDR = address;
|
||||
|
|
@ -644,21 +634,6 @@ bool SERCOM::isBusOwnerWIRE( void )
|
|||
return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_OWNER_STATE;
|
||||
}
|
||||
|
||||
bool SERCOM::isBusUnknownWIRE( void )
|
||||
{
|
||||
return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_UNKNOWN_STATE;
|
||||
}
|
||||
|
||||
bool SERCOM::isArbLostWIRE( void )
|
||||
{
|
||||
return sercom->I2CM.STATUS.bit.ARBLOST == 1;
|
||||
}
|
||||
|
||||
bool SERCOM::isBusBusyWIRE( void )
|
||||
{
|
||||
return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_BUSY_STATE;
|
||||
}
|
||||
|
||||
bool SERCOM::isDataReadyWIRE( void )
|
||||
{
|
||||
return sercom->I2CS.INTFLAG.bit.DRDY;
|
||||
|
|
|
|||
|
|
@ -225,9 +225,6 @@ class SERCOM
|
|||
bool isSlaveWIRE( void ) ;
|
||||
bool isBusIdleWIRE( void ) ;
|
||||
bool isBusOwnerWIRE( void ) ;
|
||||
bool isBusUnknownWIRE( void ) ;
|
||||
bool isArbLostWIRE( void );
|
||||
bool isBusBusyWIRE( void );
|
||||
bool isDataReadyWIRE( void ) ;
|
||||
bool isStopDetectedWIRE( void ) ;
|
||||
bool isRestartDetectedWIRE( void ) ;
|
||||
|
|
@ -248,7 +245,7 @@ class SERCOM
|
|||
uint32_t getFreqRef(void) { return freqRef; };
|
||||
#else
|
||||
// The equivalent SAMD21 dummy functions...
|
||||
void setClockSource(int8_t idx, SercomClockSource src, bool core) { (void)idx; (void)src; (void)core; };
|
||||
void setClockSource(int8_t idx, SercomClockSource src, bool core) { };
|
||||
SercomClockSource getClockSource(void) { return SERCOM_CLOCK_SOURCE_FCPU; };
|
||||
uint32_t getFreqRef(void) { return F_CPU; };
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ int Stream::timedRead()
|
|||
do {
|
||||
c = read();
|
||||
if (c >= 0) return c;
|
||||
yield(); // running TinyUSB task
|
||||
} while(millis() - _startMillis < _timeout);
|
||||
return -1; // -1 indicates timeout
|
||||
}
|
||||
|
|
@ -48,7 +47,6 @@ int Stream::timedPeek()
|
|||
do {
|
||||
c = peek();
|
||||
if (c >= 0) return c;
|
||||
yield(); // running TinyUSB task
|
||||
} while(millis() - _startMillis < _timeout);
|
||||
return -1; // -1 indicates timeout
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit e7b892095f2bb5d8bef6a748238369bdd268ed5e
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019, hathach for Adafruit
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef USE_TINYUSB
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Adafruit_TinyUSB_Core.h"
|
||||
#include <Reset.h> // Needed for auto-reset with 1200bps port touch
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Forward USB interrupt events to TinyUSB IRQ Handler
|
||||
//--------------------------------------------------------------------+
|
||||
extern "C"
|
||||
{
|
||||
#if defined(__SAMD51__)
|
||||
|
||||
void USB_0_Handler (void) { tud_int_handler(0); }
|
||||
void USB_1_Handler (void) { tud_int_handler(0); }
|
||||
void USB_2_Handler (void) { tud_int_handler(0); }
|
||||
void USB_3_Handler (void) { tud_int_handler(0); }
|
||||
|
||||
#else
|
||||
|
||||
void USB_Handler(void) { tud_int_handler(0); }
|
||||
|
||||
#endif
|
||||
} // extern C
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
static void usb_hardware_init(void);
|
||||
|
||||
#if CFG_TUSB_DEBUG
|
||||
extern "C" int serial1_printf(const char *__restrict format, ...)
|
||||
{
|
||||
char buf[PRINTF_BUF];
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vsnprintf(buf, sizeof(buf), format, ap);
|
||||
Serial1.write(buf);
|
||||
va_end(ap);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Core Init & Touch1200
|
||||
//--------------------------------------------------------------------+
|
||||
void Adafruit_TinyUSB_Core_init(void)
|
||||
{
|
||||
#if CFG_TUSB_DEBUG
|
||||
Serial1.begin(115200);
|
||||
serial1_printf("TinyUSB debugging with Serial1\n");
|
||||
#endif
|
||||
|
||||
Serial.setStringDescriptor("TinyUSB Serial");
|
||||
USBDevice.addInterface(Serial);
|
||||
USBDevice.setID(USB_VID, USB_PID);
|
||||
USBDevice.begin();
|
||||
|
||||
usb_hardware_init();
|
||||
|
||||
// Init tinyusb stack
|
||||
tusb_init();
|
||||
}
|
||||
|
||||
void Adafruit_TinyUSB_Core_touch1200(void)
|
||||
{
|
||||
initiateReset(250);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Adafruit_USBD_Device platform dependent
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t Adafruit_USBD_Device::getSerialDescriptor(uint16_t* serial_str)
|
||||
{
|
||||
enum { SERIAL_BYTE_LEN = 16 };
|
||||
|
||||
#ifdef __SAMD51__
|
||||
uint32_t* id_addresses[4] = {(uint32_t *) 0x008061FC, (uint32_t *) 0x00806010,
|
||||
(uint32_t *) 0x00806014, (uint32_t *) 0x00806018};
|
||||
#else // samd21
|
||||
uint32_t* id_addresses[4] = {(uint32_t *) 0x0080A00C, (uint32_t *) 0x0080A040,
|
||||
(uint32_t *) 0x0080A044, (uint32_t *) 0x0080A048};
|
||||
|
||||
#endif
|
||||
|
||||
uint8_t raw_id[SERIAL_BYTE_LEN];
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
for (int k=0; k<4; k++) {
|
||||
raw_id[4 * i + (3 - k)] = (*(id_addresses[i]) >> k * 8) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static const char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(raw_id); i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
uint8_t nibble = (raw_id[i] >> (j * 4)) & 0xf;
|
||||
// Strings are UTF-16-LE encoded.
|
||||
serial_str[i * 2 + (1 - j)] = nibble_to_hex[nibble];
|
||||
}
|
||||
}
|
||||
|
||||
return sizeof(raw_id)*2;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Init usb hardware when starting up. Softdevice is not enabled yet
|
||||
static void usb_hardware_init(void)
|
||||
{
|
||||
#ifdef PIN_LED_TXL
|
||||
// txLEDPulse = 0;
|
||||
pinMode(PIN_LED_TXL, OUTPUT);
|
||||
digitalWrite(PIN_LED_TXL, HIGH);
|
||||
#endif
|
||||
|
||||
#ifdef PIN_LED_RXL
|
||||
// rxLEDPulse = 0;
|
||||
pinMode(PIN_LED_RXL, OUTPUT);
|
||||
digitalWrite(PIN_LED_RXL, HIGH);
|
||||
#endif
|
||||
|
||||
/* Enable USB clock */
|
||||
#if defined(__SAMD51__)
|
||||
MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB;
|
||||
MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB;
|
||||
|
||||
// Set up the USB DP/DN pins
|
||||
PORT->Group[0].PINCFG[PIN_PA24H_USB_DM].bit.PMUXEN = 1;
|
||||
PORT->Group[0].PMUX[PIN_PA24H_USB_DM/2].reg &= ~(0xF << (4 * (PIN_PA24H_USB_DM & 0x01u)));
|
||||
PORT->Group[0].PMUX[PIN_PA24H_USB_DM/2].reg |= MUX_PA24H_USB_DM << (4 * (PIN_PA24H_USB_DM & 0x01u));
|
||||
PORT->Group[0].PINCFG[PIN_PA25H_USB_DP].bit.PMUXEN = 1;
|
||||
PORT->Group[0].PMUX[PIN_PA25H_USB_DP/2].reg &= ~(0xF << (4 * (PIN_PA25H_USB_DP & 0x01u)));
|
||||
PORT->Group[0].PMUX[PIN_PA25H_USB_DP/2].reg |= MUX_PA25H_USB_DP << (4 * (PIN_PA25H_USB_DP & 0x01u));
|
||||
|
||||
|
||||
GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
|
||||
|
||||
NVIC_SetPriority(USB_0_IRQn, 0UL);
|
||||
NVIC_SetPriority(USB_1_IRQn, 0UL);
|
||||
NVIC_SetPriority(USB_2_IRQn, 0UL);
|
||||
NVIC_SetPriority(USB_3_IRQn, 0UL);
|
||||
#else
|
||||
PM->APBBMASK.reg |= PM_APBBMASK_USB;
|
||||
|
||||
// Set up the USB DP/DN pins
|
||||
PORT->Group[0].PINCFG[PIN_PA24G_USB_DM].bit.PMUXEN = 1;
|
||||
PORT->Group[0].PMUX[PIN_PA24G_USB_DM/2].reg &= ~(0xF << (4 * (PIN_PA24G_USB_DM & 0x01u)));
|
||||
PORT->Group[0].PMUX[PIN_PA24G_USB_DM/2].reg |= MUX_PA24G_USB_DM << (4 * (PIN_PA24G_USB_DM & 0x01u));
|
||||
PORT->Group[0].PINCFG[PIN_PA25G_USB_DP].bit.PMUXEN = 1;
|
||||
PORT->Group[0].PMUX[PIN_PA25G_USB_DP/2].reg &= ~(0xF << (4 * (PIN_PA25G_USB_DP & 0x01u)));
|
||||
PORT->Group[0].PMUX[PIN_PA25G_USB_DP/2].reg |= MUX_PA25G_USB_DP << (4 * (PIN_PA25G_USB_DP & 0x01u));
|
||||
|
||||
// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference)
|
||||
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(6) | // Generic Clock Multiplexer 6
|
||||
GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
|
||||
GCLK_CLKCTRL_CLKEN;
|
||||
while (GCLK->STATUS.bit.SYNCBUSY)
|
||||
;
|
||||
|
||||
NVIC_SetPriority((IRQn_Type) USB_IRQn, 0UL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // USE_TINYUSB
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018, hathach for Adafruit
|
||||
*
|
||||
* 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 _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
#ifdef __SAMD51__
|
||||
#define CFG_TUSB_MCU OPT_MCU_SAMD51
|
||||
#else
|
||||
#define CFG_TUSB_MCU OPT_MCU_SAMD21
|
||||
#endif
|
||||
|
||||
#ifdef USE_TINYUSB
|
||||
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
|
||||
#else
|
||||
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_NONE
|
||||
#endif
|
||||
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
|
||||
#define CFG_TUSB_DEBUG 0
|
||||
#if CFG_TUSB_DEBUG
|
||||
#define tu_printf serial1_printf
|
||||
extern int serial1_printf(const char *__restrict __format, ...);
|
||||
#endif
|
||||
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#define CFG_TUD_ENDOINT0_SIZE 64
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_CDC 1
|
||||
#define CFG_TUD_MSC 1
|
||||
#define CFG_TUD_HID 1
|
||||
#define CFG_TUD_MIDI 1
|
||||
#define CFG_TUD_VENDOR 1
|
||||
|
||||
// CDC FIFO size of TX and RX
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE 256
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE 256
|
||||
|
||||
// MSC Buffer size of Device Mass storage
|
||||
#define CFG_TUD_MSC_BUFSIZE 512
|
||||
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_BUFSIZE 64
|
||||
|
||||
// MIDI FIFO size of TX and RX
|
||||
#define CFG_TUD_MIDI_RX_BUFSIZE 128
|
||||
#define CFG_TUD_MIDI_TX_BUFSIZE 128
|
||||
|
||||
// Vendor FIFO size of TX and RX
|
||||
#define CFG_TUD_VENDOR_RX_BUFSIZE 64
|
||||
#define CFG_TUD_VENDOR_TX_BUFSIZE 64
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
||||
|
|
@ -20,6 +20,12 @@
|
|||
#include "Tone.h"
|
||||
#include "variant.h"
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.SYNCBUSY.bit.ENABLE);
|
||||
#else
|
||||
#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY);
|
||||
#endif
|
||||
|
||||
uint32_t toneMaxFrequency = F_CPU / 2;
|
||||
uint32_t lastOutputPin = 0xFFFFFFFF;
|
||||
|
||||
|
|
@ -31,24 +37,22 @@ volatile bool toneIsActive = false;
|
|||
volatile bool firstTimeRunning = false;
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
#define TONE_TC TC0
|
||||
#define TONE_TC_IRQn TC0_IRQn
|
||||
#define TONE_TC_GCLK_ID TC0_GCLK_ID
|
||||
#define Tone_Handler TC0_Handler
|
||||
|
||||
#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.SYNCBUSY.bit.ENABLE);
|
||||
|
||||
#define TONE_TC TC3
|
||||
#define TONE_TC_IRQn TC3_IRQn
|
||||
#define TONE_TC_GCLK_ID TC3_GCLK_ID
|
||||
#else
|
||||
#define TONE_TC TC5
|
||||
#define TONE_TC_IRQn TC5_IRQn
|
||||
#define Tone_Handler TC5_Handler
|
||||
|
||||
#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY);
|
||||
#define TONE_TC TC5
|
||||
#define TONE_TC_IRQn TC5_IRQn
|
||||
#endif
|
||||
|
||||
#define TONE_TC_TOP 0xFFFF
|
||||
#define TONE_TC_CHANNEL 0
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
void TC2_Handler (void) __attribute__ ((weak, alias("Tone_Handler")));
|
||||
#else
|
||||
void TC5_Handler (void) __attribute__ ((weak, alias("Tone_Handler")));
|
||||
#endif
|
||||
|
||||
static inline void resetTC (Tc* TCx)
|
||||
{
|
||||
// Disable TCx
|
||||
|
|
@ -68,14 +72,6 @@ void toneAccurateClock (uint32_t accurateSystemCoreClockFrequency)
|
|||
|
||||
void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration)
|
||||
{
|
||||
|
||||
// Avoid divide by zero error by calling 'noTone' instead
|
||||
if (frequency == 0)
|
||||
{
|
||||
noTone(outputPin);
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure interrupt request
|
||||
NVIC_DisableIRQ(TONE_TC_IRQn);
|
||||
NVIC_ClearPendingIRQ(TONE_TC_IRQn);
|
||||
|
|
@ -84,16 +80,19 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration)
|
|||
{
|
||||
firstTimeRunning = true;
|
||||
|
||||
NVIC_SetPriority(TONE_TC_IRQn, 5);
|
||||
NVIC_SetPriority(TONE_TC_IRQn, 0);
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
GCLK->PCHCTRL[TONE_TC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
|
||||
GCLK->PCHCTRL[TONE_TC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
|
||||
#else
|
||||
// Enable GCLK for TC4 and TC5 (timer counter input clock)
|
||||
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5));
|
||||
while (GCLK->STATUS.bit.SYNCBUSY);
|
||||
#endif
|
||||
}
|
||||
|
||||
//if it's a rest, set to 1Hz (below audio range)
|
||||
frequency = (frequency > 0 ? frequency : 1);
|
||||
|
||||
if (toneIsActive && (outputPin != lastOutputPin))
|
||||
noTone(lastOutputPin);
|
||||
|
|
@ -180,19 +179,9 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration)
|
|||
|
||||
void noTone (uint32_t outputPin)
|
||||
{
|
||||
/* 'tone' need to run at least once in order to enable GCLK for
|
||||
* the timers used for the tone-functionality. If 'noTone' is called
|
||||
* without ever calling 'tone' before then 'WAIT_TC16_REGS_SYNC(TCx)'
|
||||
* will wait infinitely. The variable 'firstTimeRunning' is set the
|
||||
* 1st time 'tone' is set so it can be used to detect wether or not
|
||||
* 'tone' has been called before.
|
||||
*/
|
||||
if(firstTimeRunning)
|
||||
{
|
||||
resetTC(TONE_TC);
|
||||
digitalWrite(outputPin, LOW);
|
||||
toneIsActive = false;
|
||||
}
|
||||
resetTC(TONE_TC);
|
||||
digitalWrite(outputPin, LOW);
|
||||
toneIsActive = false;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef USE_TINYUSB
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Reset.h> // Needed for auto-reset with 1200bps port touch
|
||||
|
||||
|
|
@ -262,5 +260,3 @@ Serial_::operator bool()
|
|||
Serial_ Serial(USBDevice);
|
||||
|
||||
#endif
|
||||
|
||||
#endif // USE_TINYUSB
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef USE_TINYUSB
|
||||
|
||||
#include "USBAPI.h"
|
||||
#include "USBDesc.h"
|
||||
#include "USBCore.h"
|
||||
|
|
@ -115,6 +113,4 @@ PluggableUSB_::PluggableUSB_() : lastIf(CDC_ACM_INTERFACE + CDC_INTERFACE_COUNT)
|
|||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // USE_TINYUSB
|
||||
#endif
|
||||
|
|
@ -5,7 +5,6 @@
|
|||
* Author: deanm
|
||||
*/
|
||||
|
||||
#ifndef USE_TINYUSB
|
||||
|
||||
#include "SAMD21_USBDevice.h"
|
||||
|
||||
|
|
@ -37,5 +36,3 @@ void USBDevice_SAMD21G18x::calibrate() {
|
|||
usb.PADCAL.bit.TRANSP = pad_transp;
|
||||
usb.PADCAL.bit.TRIM = pad_trim;
|
||||
}
|
||||
|
||||
#endif // USE_TINYUSB
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef USE_TINYUSB
|
||||
|
||||
#if defined(USBCON)
|
||||
|
||||
#include <Arduino.h>
|
||||
|
|
@ -244,25 +244,26 @@ bool USBDeviceClass::sendDescriptor(USBSetup &setup)
|
|||
}
|
||||
else if (setup.wValueL == ISERIAL) {
|
||||
#ifdef PLUGGABLE_USB_ENABLED
|
||||
#ifdef __SAMD51__
|
||||
#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x008061FC)
|
||||
#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x00806010)
|
||||
#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x00806014)
|
||||
#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x00806018)
|
||||
#else // samd21
|
||||
#if defined(__SAMD51__)
|
||||
char name[ISERIAL_MAX_LEN];
|
||||
PluggableUSB().getShortName(name);
|
||||
return sendStringDescriptor((uint8_t*)name, setup.wLength);
|
||||
#else
|
||||
// from section 9.3.3 of the datasheet
|
||||
#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C)
|
||||
#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040)
|
||||
#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044)
|
||||
#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048)
|
||||
#endif
|
||||
|
||||
char name[ISERIAL_MAX_LEN];
|
||||
utox8(SERIAL_NUMBER_WORD_0, &name[0]);
|
||||
utox8(SERIAL_NUMBER_WORD_1, &name[8]);
|
||||
utox8(SERIAL_NUMBER_WORD_2, &name[16]);
|
||||
utox8(SERIAL_NUMBER_WORD_3, &name[24]);
|
||||
name[32] = '\0';
|
||||
|
||||
PluggableUSB().getShortName(&name[32]);
|
||||
return sendStringDescriptor((uint8_t*)name, setup.wLength);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
|
|
@ -877,7 +878,6 @@ bool USBDeviceClass::handleStandardSetup(USBSetup &setup)
|
|||
sendZlp(0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case SET_ADDRESS:
|
||||
setAddress(setup.wValueL);
|
||||
|
|
@ -1039,4 +1039,3 @@ void USBDeviceClass::ISRHandler()
|
|||
USBDeviceClass USBDevice;
|
||||
|
||||
#endif
|
||||
#endif // USE_TINYUSB
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef USE_TINYUSB
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
|
@ -554,5 +553,3 @@ uint32_t UHD_Pipe_Is_Transfer_Complete(uint32_t ul_pipe, uint32_t ul_token_type)
|
|||
// }
|
||||
|
||||
#endif // HOST_DEFINED
|
||||
|
||||
#endif // USE_TINYUSB
|
||||
|
|
|
|||
|
|
@ -250,9 +250,8 @@ void detachInterrupt(uint32_t pin)
|
|||
* External Interrupt Controller NVIC Interrupt Handler
|
||||
*/
|
||||
#if defined(__SAMD51__)
|
||||
void InterruptHandler(uint32_t unused_i)
|
||||
void InterruptHandler(uint32_t i)
|
||||
{
|
||||
(void)unused_i;
|
||||
// Calling the routine directly from -here- takes about 1us
|
||||
// Depending on where you are in the list it will take longer
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ extern "C" {
|
|||
#define FALLING 3
|
||||
#define RISING 4
|
||||
|
||||
//#define DEFAULT 1
|
||||
#define DEFAULT 1
|
||||
#define EXTERNAL 0
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
|
|
|
|||
|
|
@ -57,8 +57,6 @@ typedef enum _EAnalogChannel
|
|||
ADC_Channel19=19,
|
||||
DAC_Channel0,
|
||||
DAC_Channel1,
|
||||
ADC_Channel_Bandgap=0x1B,
|
||||
ADC_Channel_PTAT=0x1C,
|
||||
} EAnalogChannel ;
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
|
|
@ -101,16 +99,10 @@ typedef enum _ETCChannel
|
|||
TCC0_CH3 = (0<<8)|(3),
|
||||
TCC0_CH4 = (0<<8)|(4),
|
||||
TCC0_CH5 = (0<<8)|(5),
|
||||
TCC0_CH6 = (0<<8)|(6),
|
||||
TCC0_CH7 = (0<<8)|(7),
|
||||
TCC1_CH0 = (1<<8)|(0),
|
||||
TCC1_CH1 = (1<<8)|(1),
|
||||
TCC1_CH2 = (1<<8)|(2),
|
||||
TCC1_CH3 = (1<<8)|(3),
|
||||
TCC1_CH4 = (1<<8)|(4),
|
||||
TCC1_CH5 = (1<<8)|(5),
|
||||
TCC1_CH6 = (1<<8)|(6),
|
||||
TCC1_CH7 = (1<<8)|(7),
|
||||
TCC2_CH0 = (2<<8)|(0),
|
||||
TCC2_CH1 = (2<<8)|(1),
|
||||
TCC2_CH2 = (2<<8)|(2),
|
||||
|
|
@ -130,10 +122,6 @@ typedef enum _ETCChannel
|
|||
TC4_CH1 = (9<<8)|(1),
|
||||
TC5_CH0 = (10<<8)|(0),
|
||||
TC5_CH1 = (10<<8)|(1),
|
||||
TC6_CH0 = (11<<8)|(0),
|
||||
TC6_CH1 = (11<<8)|(1),
|
||||
TC7_CH0 = (12<<8)|(0),
|
||||
TC7_CH1 = (12<<8)|(1),
|
||||
} ETCChannel ;
|
||||
|
||||
#elif defined(__SAMD51P19A__) || defined(__SAMD51P20A__)
|
||||
|
|
@ -210,12 +198,6 @@ typedef enum _ETCChannel
|
|||
TC4_CH1 = (4<<8)|(1),
|
||||
TC5_CH0 = (5<<8)|(0),
|
||||
TC5_CH1 = (5<<8)|(1),
|
||||
#if defined (__SAMD21J18A__)
|
||||
TC6_CH0 = (6<<8)|(0),
|
||||
TC6_CH1 = (6<<8)|(1),
|
||||
TC7_CH0 = (7<<8)|(0),
|
||||
TC7_CH1 = (7<<8)|(1),
|
||||
#endif // __SAMD21J18A__
|
||||
} ETCChannel ;
|
||||
|
||||
// Definitions for PWM channels
|
||||
|
|
|
|||
|
|
@ -25,14 +25,8 @@
|
|||
#ifndef _IO_H_
|
||||
#define _IO_H_
|
||||
|
||||
#ifdef __SAMD51__
|
||||
#define RAMSTART (HSRAM_ADDR)
|
||||
#define RAMSIZE (HSRAM_SIZE)
|
||||
#else
|
||||
#define RAMSTART (HMCRAMC0_ADDR)
|
||||
#define RAMSIZE (HMCRAMC0_SIZE)
|
||||
#endif
|
||||
|
||||
#define RAMSTART (HMCRAMC0_ADDR)
|
||||
#define RAMSIZE (HMCRAMC0_SIZE)
|
||||
#define RAMEND (RAMSTART + RAMSIZE - 1)
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
static void __empty() {
|
||||
// Empty
|
||||
}
|
||||
|
||||
void yield(void) __attribute__ ((weak, alias("__empty")));
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -39,10 +39,7 @@ int main( void )
|
|||
initVariant();
|
||||
|
||||
delay(1);
|
||||
|
||||
#if defined(USE_TINYUSB)
|
||||
Adafruit_TinyUSB_Core_init();
|
||||
#elif defined(USBCON)
|
||||
#if defined(USBCON)
|
||||
USBDevice.init();
|
||||
USBDevice.attach();
|
||||
#endif
|
||||
|
|
@ -52,21 +49,8 @@ int main( void )
|
|||
for (;;)
|
||||
{
|
||||
loop();
|
||||
yield(); // yield run usb background task
|
||||
|
||||
if (serialEventRun) serialEventRun();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(USE_TINYUSB)
|
||||
|
||||
// run TinyUSB background task when yield()
|
||||
extern "C" void yield(void)
|
||||
{
|
||||
tud_task();
|
||||
tud_cdc_write_flush();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -164,19 +164,18 @@ void arm_float_to_q12_20(float *pIn, q31_t * pOut, uint32_t numSamples)
|
|||
uint32_t arm_compare_fixed_q15(q15_t *pIn, q15_t * pOut, uint32_t numSamples)
|
||||
{
|
||||
uint32_t i;
|
||||
int32_t diff;
|
||||
uint32_t diffCrnt = 0;
|
||||
int32_t diff, diffCrnt = 0;
|
||||
uint32_t maxDiff = 0;
|
||||
|
||||
for (i = 0; i < numSamples; i++)
|
||||
{
|
||||
diff = pIn[i] - pOut[i];
|
||||
diffCrnt = (uint32_t)( (diff > 0) ? diff : -diff );
|
||||
diff = pIn[i] - pOut[i];
|
||||
diffCrnt = (diff > 0) ? diff : -diff;
|
||||
|
||||
if(diffCrnt > maxDiff)
|
||||
{
|
||||
maxDiff = diffCrnt;
|
||||
}
|
||||
if(diffCrnt > maxDiff)
|
||||
{
|
||||
maxDiff = diffCrnt;
|
||||
}
|
||||
}
|
||||
|
||||
return(maxDiff);
|
||||
|
|
@ -193,19 +192,18 @@ uint32_t arm_compare_fixed_q15(q15_t *pIn, q15_t * pOut, uint32_t numSamples)
|
|||
uint32_t arm_compare_fixed_q31(q31_t *pIn, q31_t * pOut, uint32_t numSamples)
|
||||
{
|
||||
uint32_t i;
|
||||
int32_t diff;
|
||||
uint32_t diffCrnt = 0;
|
||||
int32_t diff, diffCrnt = 0;
|
||||
uint32_t maxDiff = 0;
|
||||
|
||||
for (i = 0; i < numSamples; i++)
|
||||
{
|
||||
diff = pIn[i] - pOut[i];
|
||||
diffCrnt = (uint32_t)( (diff > 0) ? diff : -diff );
|
||||
diff = pIn[i] - pOut[i];
|
||||
diffCrnt = (diff > 0) ? diff : -diff;
|
||||
|
||||
if(diffCrnt > maxDiff)
|
||||
{
|
||||
maxDiff = diffCrnt;
|
||||
}
|
||||
if(diffCrnt > maxDiff)
|
||||
{
|
||||
maxDiff = diffCrnt;
|
||||
}
|
||||
}
|
||||
|
||||
return(maxDiff);
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ uint32_t pulseIn(uint32_t pin, uint32_t state, uint32_t timeout)
|
|||
* No assembly required, no conversion of loop counts to times (which is
|
||||
* worrisome in the presence of cache.)
|
||||
*/
|
||||
const volatile uint32_t *port = &(PORT->Group[p.ulPort].IN.reg);
|
||||
volatile uint32_t *port = &(PORT->Group[p.ulPort].IN.reg);
|
||||
uint32_t usCallStart; // microseconds at start of call, for timeout.
|
||||
uint32_t usPulseStart; // microseconds at start of measured pulse.
|
||||
usCallStart = usPulseStart = micros();
|
||||
usCallStart = micros();
|
||||
// wait for any previous pulse to end
|
||||
while ((*port & bit) == stateMask) {
|
||||
if (micros() - usCallStart > timeout)
|
||||
|
|
|
|||
|
|
@ -261,47 +261,6 @@ void SystemInit( void )
|
|||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
|
||||
/* ----------------------------------------------------------------------------------------------
|
||||
* 5) Load AC factory calibration values
|
||||
*/
|
||||
|
||||
uint32_t bias0 = (*((uint32_t *)AC_FUSES_BIAS0_ADDR) & AC_FUSES_BIAS0_Msk) >> AC_FUSES_BIAS0_Pos;
|
||||
AC->CALIB.reg = AC_CALIB_BIAS0(bias0);
|
||||
|
||||
/* ----------------------------------------------------------------------------------------------
|
||||
* 6) Load ADC factory calibration values
|
||||
*/
|
||||
|
||||
// ADC0 Bias Calibration
|
||||
uint32_t biascomp = (*((uint32_t *)ADC0_FUSES_BIASCOMP_ADDR) & ADC0_FUSES_BIASCOMP_Msk) >> ADC0_FUSES_BIASCOMP_Pos;
|
||||
uint32_t biasr2r = (*((uint32_t *)ADC0_FUSES_BIASR2R_ADDR) & ADC0_FUSES_BIASR2R_Msk) >> ADC0_FUSES_BIASR2R_Pos;
|
||||
uint32_t biasref = (*((uint32_t *)ADC0_FUSES_BIASREFBUF_ADDR) & ADC0_FUSES_BIASREFBUF_Msk) >> ADC0_FUSES_BIASREFBUF_Pos;
|
||||
|
||||
ADC0->CALIB.reg = ADC_CALIB_BIASREFBUF(biasref)
|
||||
| ADC_CALIB_BIASR2R(biasr2r)
|
||||
| ADC_CALIB_BIASCOMP(biascomp);
|
||||
|
||||
// ADC1 Bias Calibration
|
||||
biascomp = (*((uint32_t *)ADC1_FUSES_BIASCOMP_ADDR) & ADC1_FUSES_BIASCOMP_Msk) >> ADC1_FUSES_BIASCOMP_Pos;
|
||||
biasr2r = (*((uint32_t *)ADC1_FUSES_BIASR2R_ADDR) & ADC1_FUSES_BIASR2R_Msk) >> ADC1_FUSES_BIASR2R_Pos;
|
||||
biasref = (*((uint32_t *)ADC1_FUSES_BIASREFBUF_ADDR) & ADC1_FUSES_BIASREFBUF_Msk) >> ADC1_FUSES_BIASREFBUF_Pos;
|
||||
|
||||
ADC1->CALIB.reg = ADC_CALIB_BIASREFBUF(biasref)
|
||||
| ADC_CALIB_BIASR2R(biasr2r)
|
||||
| ADC_CALIB_BIASCOMP(biascomp);
|
||||
|
||||
/* ----------------------------------------------------------------------------------------------
|
||||
* 7) Load USB factory calibration values
|
||||
*/
|
||||
|
||||
//USB Calibration
|
||||
uint32_t usbtransn = (*((uint32_t *)USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos;
|
||||
uint32_t usbtransp = (*((uint32_t *)USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos;
|
||||
uint32_t usbtrim = (*((uint32_t *)USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
|
||||
USB->DEVICE.PADCAL.reg = USB_PADCAL_TRIM(usbtrim)
|
||||
| USB_PADCAL_TRANSN(usbtransn)
|
||||
| USB_PADCAL_TRANSP(usbtransp);
|
||||
|
||||
//*************** END SAMD51 *************************//
|
||||
|
||||
#else
|
||||
|
|
@ -577,3 +536,4 @@ void SystemInit( void )
|
|||
NVMCTRL->CTRLB.bit.MANW = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ void init( void )
|
|||
PM->APBCMASK.reg |= PM_APBCMASK_SERCOM0 | PM_APBCMASK_SERCOM1 | PM_APBCMASK_SERCOM2 | PM_APBCMASK_SERCOM3 | PM_APBCMASK_SERCOM4 | PM_APBCMASK_SERCOM5 ;
|
||||
|
||||
// Clock TC/TCC for Pulse and Analog
|
||||
PM->APBCMASK.reg |= PM_APBCMASK_TCC0 | PM_APBCMASK_TCC1 | PM_APBCMASK_TCC2 | PM_APBCMASK_TC3 | PM_APBCMASK_TC4 | PM_APBCMASK_TC5 | PM_APBCMASK_TC6 | PM_APBCMASK_TC7;
|
||||
PM->APBCMASK.reg |= PM_APBCMASK_TCC0 | PM_APBCMASK_TCC1 | PM_APBCMASK_TCC2 | PM_APBCMASK_TC3 | PM_APBCMASK_TC4 | PM_APBCMASK_TC5 ;
|
||||
|
||||
// ATSAMR, for example, doesn't have a DAC
|
||||
#ifdef PM_APBCMASK_DAC
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ static int _writeResolution = 12;
|
|||
static int _dacResolution = 12;
|
||||
#else
|
||||
static int _writeResolution = 8;
|
||||
//static int _dacResolution = 10;
|
||||
static int _dacResolution = 10;
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -280,8 +280,8 @@ uint32_t analogRead(uint32_t pin)
|
|||
#ifdef DAC
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
if (pin == PIN_DAC0 || pin == PIN_DAC1) { // Disable DAC, if analogWrite(A0,dval) used previously the DAC is enabled
|
||||
uint8_t channel = (pin == PIN_DAC0 ? 0 : 1);
|
||||
if (pin == A0 || pin == A1) { // Disable DAC, if analogWrite(A0,dval) used previously the DAC is enabled
|
||||
uint8_t channel = (pin == PIN_A0 ? 0 : 1);
|
||||
|
||||
if(dacEnabled[channel]){
|
||||
dacEnabled[channel] = false;
|
||||
|
|
@ -298,7 +298,7 @@ uint32_t analogRead(uint32_t pin)
|
|||
|
||||
while (DAC->SYNCBUSY.bit.ENABLE);
|
||||
#else
|
||||
if (pin == PIN_DAC0) { // Disable DAC, if analogWrite(A0,dval) used previously the DAC is enabled
|
||||
if (pin == A0) { // Disable DAC, if analogWrite(A0,dval) used previously the DAC is enabled
|
||||
syncDAC();
|
||||
|
||||
DAC->CTRLA.bit.ENABLE = 0x00; // Disable DAC
|
||||
|
|
@ -410,9 +410,9 @@ void analogWrite(uint32_t pin, uint32_t value)
|
|||
{
|
||||
// DAC handling code
|
||||
#if defined(__SAMD51__)
|
||||
if (pin == PIN_DAC0 || pin == PIN_DAC1) { // 2 DACs on A0 (PA02) and A1 (PA05)
|
||||
if (pin == PIN_A0 || pin == PIN_A1) { // 2 DACs on A0 (PA02) and A1 (PA05)
|
||||
#else
|
||||
if (pin == PIN_DAC0) { // Only 1 DAC on A0 (PA02)
|
||||
if (pin == PIN_A0) { // Only 1 DAC on A0 (PA02)
|
||||
#endif
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
|
|
@ -420,7 +420,7 @@ void analogWrite(uint32_t pin, uint32_t value)
|
|||
value = mapResolution(value, _writeResolution, _dacResolution);
|
||||
|
||||
|
||||
uint8_t channel = (pin == PIN_DAC0 ? 0 : 1);
|
||||
uint8_t channel = (pin == PIN_A0 ? 0 : 1);
|
||||
|
||||
pinPeripheral(pin, PIO_ANALOG);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,43 +30,39 @@ void pinMode( uint32_t ulPin, uint32_t ulMode )
|
|||
return ;
|
||||
}
|
||||
|
||||
EPortType port = g_APinDescription[ulPin].ulPort;
|
||||
uint32_t pin = g_APinDescription[ulPin].ulPin;
|
||||
uint32_t pinMask = (1ul << pin);
|
||||
|
||||
// Set pin mode according to chapter '22.6.3 I/O Pin Configuration'
|
||||
switch ( ulMode )
|
||||
{
|
||||
case INPUT:
|
||||
// Set pin to input mode
|
||||
PORT->Group[port].PINCFG[pin].reg=(uint8_t)(PORT_PINCFG_INEN) ;
|
||||
PORT->Group[port].DIRCLR.reg = pinMask ;
|
||||
PORT->Group[g_APinDescription[ulPin].ulPort].PINCFG[g_APinDescription[ulPin].ulPin].reg=(uint8_t)(PORT_PINCFG_INEN) ;
|
||||
PORT->Group[g_APinDescription[ulPin].ulPort].DIRCLR.reg = (uint32_t)(1<<g_APinDescription[ulPin].ulPin) ;
|
||||
break ;
|
||||
|
||||
case INPUT_PULLUP:
|
||||
// Set pin to input mode with pull-up resistor enabled
|
||||
PORT->Group[port].PINCFG[pin].reg=(uint8_t)(PORT_PINCFG_INEN|PORT_PINCFG_PULLEN) ;
|
||||
PORT->Group[port].DIRCLR.reg = pinMask ;
|
||||
PORT->Group[g_APinDescription[ulPin].ulPort].PINCFG[g_APinDescription[ulPin].ulPin].reg=(uint8_t)(PORT_PINCFG_INEN|PORT_PINCFG_PULLEN) ;
|
||||
PORT->Group[g_APinDescription[ulPin].ulPort].DIRCLR.reg = (uint32_t)(1<<g_APinDescription[ulPin].ulPin) ;
|
||||
|
||||
// Enable pull level (cf '22.6.3.2 Input Configuration' and '22.8.7 Data Output Value Set')
|
||||
PORT->Group[port].OUTSET.reg = pinMask ;
|
||||
PORT->Group[g_APinDescription[ulPin].ulPort].OUTSET.reg = (uint32_t)(1<<g_APinDescription[ulPin].ulPin) ;
|
||||
break ;
|
||||
|
||||
case INPUT_PULLDOWN:
|
||||
// Set pin to input mode with pull-down resistor enabled
|
||||
PORT->Group[port].PINCFG[pin].reg=(uint8_t)(PORT_PINCFG_INEN|PORT_PINCFG_PULLEN) ;
|
||||
PORT->Group[port].DIRCLR.reg = pinMask ;
|
||||
PORT->Group[g_APinDescription[ulPin].ulPort].PINCFG[g_APinDescription[ulPin].ulPin].reg=(uint8_t)(PORT_PINCFG_INEN|PORT_PINCFG_PULLEN) ;
|
||||
PORT->Group[g_APinDescription[ulPin].ulPort].DIRCLR.reg = (uint32_t)(1<<g_APinDescription[ulPin].ulPin) ;
|
||||
|
||||
// Enable pull level (cf '22.6.3.2 Input Configuration' and '22.8.6 Data Output Value Clear')
|
||||
PORT->Group[port].OUTCLR.reg = pinMask ;
|
||||
PORT->Group[g_APinDescription[ulPin].ulPort].OUTCLR.reg = (uint32_t)(1<<g_APinDescription[ulPin].ulPin) ;
|
||||
break ;
|
||||
|
||||
case OUTPUT:
|
||||
// enable input, to support reading back values, with pullups disabled
|
||||
PORT->Group[port].PINCFG[pin].reg=(uint8_t)(PORT_PINCFG_INEN) ;
|
||||
PORT->Group[g_APinDescription[ulPin].ulPort].PINCFG[g_APinDescription[ulPin].ulPin].reg=(uint8_t)(PORT_PINCFG_INEN) ;
|
||||
|
||||
// Set pin to output mode
|
||||
PORT->Group[port].DIRSET.reg = pinMask ;
|
||||
PORT->Group[g_APinDescription[ulPin].ulPort].DIRSET.reg = (uint32_t)(1<<g_APinDescription[ulPin].ulPin) ;
|
||||
break ;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1,124 +0,0 @@
|
|||
import os
|
||||
import glob
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
import argparse
|
||||
|
||||
FQBN_PREFIX='adafruit:samd:adafruit_'
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='python wrapper for adafruit arduino CI workflows',
|
||||
allow_abbrev=False
|
||||
)
|
||||
parser.add_argument(
|
||||
'--all_warnings', '--Wall',
|
||||
action='store_true',
|
||||
help='build with all warnings enabled (`--warnings all`)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--warnings_do_not_cause_job_failure',
|
||||
action='store_true',
|
||||
help='failed builds will be listed as failed, but not cause job to exit with an error status',
|
||||
)
|
||||
parser.add_argument(
|
||||
'build_boards',
|
||||
metavar='board',
|
||||
nargs='*',
|
||||
help='list of boards to be built -- Note that the fqbn is created by prepending "{}"'.format(FQBN_PREFIX),
|
||||
default= [ 'metro_m0', 'metro_m4', 'circuitplayground_m0' ]
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
exit_status = 0
|
||||
success_count = 0
|
||||
fail_count = 0
|
||||
skip_count = 0
|
||||
build_format = '| {:22} | {:30} | {:9} '
|
||||
build_separator = '-' * 80
|
||||
|
||||
def errorOutputFilter(line: str):
|
||||
if len(line) == 0:
|
||||
return False
|
||||
if line.isspace(): # Note: empty string does not match here!
|
||||
return False
|
||||
# TODO: additional items to remove?
|
||||
return True
|
||||
|
||||
def build_examples(variant: str):
|
||||
global args, exit_status, success_count, fail_count, skip_count, build_format, build_separator
|
||||
|
||||
print('\n')
|
||||
print(build_separator)
|
||||
print('| {:^76} |'.format('Board ' + variant))
|
||||
print(build_separator)
|
||||
print((build_format + '| {:6} |').format('Library', 'Example', 'Result', 'Time'))
|
||||
print(build_separator)
|
||||
|
||||
fqbn = "{}{}".format(FQBN_PREFIX, variant)
|
||||
|
||||
for sketch in glob.iglob('libraries/**/*.ino', recursive=True):
|
||||
start_time = time.monotonic()
|
||||
|
||||
# Skip if contains: ".board.test.skip" or ".all.test.skip"
|
||||
# Skip if not contains: ".board.test.only" for a specific board
|
||||
sketchdir = os.path.dirname(sketch)
|
||||
if os.path.exists(sketchdir + '/.all.test.skip') or os.path.exists(sketchdir + '/.' + variant + '.test.skip'):
|
||||
success = "\033[33mskipped\033[0m "
|
||||
elif glob.glob(sketchdir+"/.*.test.only") and not os.path.exists(sketchdir + '/.build.' + variant):
|
||||
success = "\033[33mskipped\033[0m "
|
||||
else:
|
||||
# TODO - preferably, would have STDERR show up in **both** STDOUT and STDERR.
|
||||
# preferably, would use Python logging handler to get both distinct outputs and one merged output
|
||||
# for now, split STDERR when building with all warnings enabled, so can detect warning/error output.
|
||||
if args.all_warnings:
|
||||
build_result = subprocess.run("arduino-cli compile --warnings all --fqbn {} {}".format(fqbn, sketch), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
else:
|
||||
build_result = subprocess.run("arduino-cli compile --warnings default --fqbn {} {}".format(fqbn, sketch), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
# get stderr into a form where len(warningLines) indicates a true warning was output to stderr
|
||||
warningLines = [];
|
||||
if args.all_warnings and build_result.stderr:
|
||||
tmpWarningLines = build_result.stderr.decode("utf-8").splitlines()
|
||||
warningLines = list(filter(errorOutputFilter, (tmpWarningLines)))
|
||||
|
||||
if build_result.returncode != 0:
|
||||
exit_status = build_result.returncode
|
||||
success = "\033[31mfailed\033[0m "
|
||||
fail_count += 1
|
||||
elif len(warningLines) != 0:
|
||||
if not args.warnings_do_not_cause_job_failure:
|
||||
exit_status = -1
|
||||
success = "\033[31mwarnings\033[0m "
|
||||
fail_count += 1
|
||||
else:
|
||||
success = "\033[32msucceeded\033[0m"
|
||||
success_count += 1
|
||||
|
||||
build_duration = time.monotonic() - start_time
|
||||
|
||||
print((build_format + '| {:5.2f}s |').format(sketch.split(os.path.sep)[1], os.path.basename(sketch), success, build_duration))
|
||||
|
||||
if success != "\033[33mskipped\033[0m ":
|
||||
if build_result.returncode != 0:
|
||||
print(build_result.stdout.decode("utf-8"))
|
||||
if (build_result.stderr):
|
||||
print(build_result.stderr.decode("utf-8"))
|
||||
if len(warningLines) != 0:
|
||||
for line in warningLines:
|
||||
print(line)
|
||||
else:
|
||||
skip_count += 1
|
||||
|
||||
build_time = time.monotonic()
|
||||
|
||||
for board in args.build_boards:
|
||||
build_examples(board)
|
||||
|
||||
print(build_separator)
|
||||
build_time = time.monotonic() - build_time
|
||||
print("Build Summary: {} \033[32msucceeded\033[0m, {} \033[31mfailed\033[0m, {} \033[33mskipped\033[0m and took {:.2f}s".format(success_count, fail_count, skip_count, build_time))
|
||||
print(build_separator)
|
||||
|
||||
sys.exit(exit_status)
|
||||
|
|
@ -17,7 +17,6 @@ volatile bool transfer_is_done = false; // Done yet?
|
|||
|
||||
// Callback for end-of-DMA-transfer
|
||||
void dma_callback(Adafruit_ZeroDMA *dma) {
|
||||
(void)dma; // avoid compiler warning about unused function parameter
|
||||
transfer_is_done = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ volatile bool transfer_is_done = false; // Done yet?
|
|||
|
||||
// Callback for end-of-DMA-transfer
|
||||
void dma_callback(Adafruit_ZeroDMA *dma) {
|
||||
(void)dma; // avoid compiler warning about unused parameter
|
||||
transfer_is_done = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ volatile bool transfer_is_done = true; // Done yet?
|
|||
|
||||
// Callback for end-of-DMA-transfer
|
||||
void dma_callback(Adafruit_ZeroDMA *dma) {
|
||||
(void)dma; // avoid compiler warning about unused parameter
|
||||
transfer_is_done = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
// fake empty header file to make Arduino IDE happy
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#include <arm_math.h>
|
||||
|
||||
arm_rfft_fast_instance_f32 plan;
|
||||
|
||||
void setup() {
|
||||
arm_rfft_fast_init_f32(&plan, 256);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
float in[256] = { 0 }, out[256] = { 0 };
|
||||
arm_rfft_fast_f32(&plan, in, out, 0);
|
||||
}
|
||||
|
|
@ -46,16 +46,7 @@ SPIClass::SPIClass(SERCOM *p_sercom, uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint
|
|||
|
||||
void SPIClass::begin()
|
||||
{
|
||||
if(!initialized) {
|
||||
interruptMode = SPI_IMODE_NONE;
|
||||
interruptSave = 0;
|
||||
interruptMask = 0;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
if(!use_dma) {
|
||||
dmaAllocate();
|
||||
}
|
||||
init();
|
||||
|
||||
// PIO init
|
||||
pinPeripheral(_uc_pinMiso, g_APinDescription[_uc_pinMiso].ulPinType);
|
||||
|
|
@ -65,6 +56,16 @@ void SPIClass::begin()
|
|||
config(DEFAULT_SPI_SETTINGS);
|
||||
}
|
||||
|
||||
void SPIClass::init()
|
||||
{
|
||||
if (initialized)
|
||||
return;
|
||||
interruptMode = SPI_IMODE_NONE;
|
||||
interruptSave = 0;
|
||||
interruptMask = 0;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void SPIClass::config(SPISettings settings)
|
||||
{
|
||||
_p_sercom->disableSPI();
|
||||
|
|
@ -79,7 +80,6 @@ void SPIClass::end()
|
|||
{
|
||||
_p_sercom->resetSPI();
|
||||
initialized = false;
|
||||
// Add DMA deallocation here
|
||||
}
|
||||
|
||||
#ifndef interruptsStatus
|
||||
|
|
@ -235,231 +235,157 @@ void SPIClass::transfer(void *buf, size_t count)
|
|||
}
|
||||
}
|
||||
|
||||
// DMA-based SPI transfer() function ---------------------------------------
|
||||
|
||||
// IMPORTANT: references to 65535 throughout the DMA code are INTENTIONAL.
|
||||
// DO NOT try to 'fix' by changing to 65536, or large transfers will fail!
|
||||
// The BTCNT value of a DMA descriptor is an unsigned 16-bit value with a
|
||||
// max of 65535. Larger transfers are handled by linked descriptors.
|
||||
|
||||
// Pointer to SPIClass object, one per DMA channel. This allows the
|
||||
// DMA callback (which has to exist outside the class context) to have
|
||||
// a reference back to the originating SPIClass object.
|
||||
// Pointer to SPIClass object, one per DMA channel.
|
||||
static SPIClass *spiPtr[DMAC_CH_NUM] = { 0 }; // Legit inits list to NULL
|
||||
|
||||
void SPIClass::dmaCallback(Adafruit_ZeroDMA *dma) {
|
||||
// dmaCallback() receives an Adafruit_ZeroDMA object. From this we can get
|
||||
// a channel number (0 to DMAC_CH_NUM-1, always unique per ZeroDMA object),
|
||||
// then locate the originating SPIClass object using array lookup, setting
|
||||
// the dma_busy element 'false' to indicate end of transfer. Doesn't matter
|
||||
// if it's a read or write transfer...both channels get pointers to it.
|
||||
// the dma_busy element 'false' to indicate end of transfer.
|
||||
spiPtr[dma->getChannel()]->dma_busy = false;
|
||||
}
|
||||
|
||||
// For read-only and read+write transfers, a callback is assigned only
|
||||
// to the read channel to indicate end-of-transfer, and the write channel's
|
||||
// callback is assigned to this nonsense function (for reasons I'm not
|
||||
// entirely sure of, setting the callback to NULL doesn't work).
|
||||
static void dmaDoNothingCallback(Adafruit_ZeroDMA *dma) { (void)dma; }
|
||||
|
||||
// This could've gone in begin(), but for the sake of organization...
|
||||
void SPIClass::dmaAllocate(void) {
|
||||
// In order to support fully non-blocking SPI transfers, DMA descriptor
|
||||
// lists must be created for the input and/or output data. Rather than
|
||||
// do this dynamically with every transfer, the lists are allocated once
|
||||
// on SPI init. Maximum list size is finite and knowable -- transfers to
|
||||
// or from RAM or from flash memory will never exceed the corresponding
|
||||
// memory size (if they do, you have bigger problems). Descriptors
|
||||
// aren't large and there's usually only a handful to a dozen, so this
|
||||
// isn't an excessive burden in exchange for big non-blocking transfers.
|
||||
uint32_t maxWriteBytes = FLASH_SIZE; // Writes can't exceed all of flash
|
||||
#if defined(__SAMD51__)
|
||||
uint32_t maxReadBytes = HSRAM_SIZE; // Reads can't exceed all of RAM
|
||||
#else
|
||||
uint32_t maxReadBytes = HMCRAMC0_SIZE;
|
||||
#endif
|
||||
if(maxReadBytes > maxWriteBytes) { // I don't think any SAMD devices
|
||||
maxWriteBytes = maxReadBytes; // have RAM > flash, but just in case
|
||||
}
|
||||
|
||||
// VITAL to alloc read channel first, assigns it a higher DMA priority!
|
||||
if(readChannel.allocate() == DMA_STATUS_OK) {
|
||||
if(writeChannel.allocate() == DMA_STATUS_OK) {
|
||||
|
||||
// Both DMA channels (read and write) allocated successfully,
|
||||
// set up transfer triggers and other basics...
|
||||
|
||||
// readChannel callback only needs to be set up once.
|
||||
// Unlike the write callback which may get switched on or off,
|
||||
// read callback stays put. In certain cases the read DMA job
|
||||
// just isn't started and the callback is a non-issue then.
|
||||
readChannel.setTrigger(getDMAC_ID_RX());
|
||||
readChannel.setAction(DMA_TRIGGER_ACTON_BEAT);
|
||||
readChannel.setCallback(dmaCallback);
|
||||
spiPtr[readChannel.getChannel()] = this;
|
||||
|
||||
writeChannel.setTrigger(getDMAC_ID_TX());
|
||||
writeChannel.setAction(DMA_TRIGGER_ACTON_BEAT);
|
||||
spiPtr[writeChannel.getChannel()] = this;
|
||||
|
||||
// One descriptor per channel has already been allocated
|
||||
// in Adafruit_ZeroDMA, this just gets pointers to them...
|
||||
firstReadDescriptor = readChannel.addDescriptor(
|
||||
(void *)getDataRegister(), // Source address (SPI data reg)
|
||||
NULL, // Dest address (set later)
|
||||
0, // Count (set later)
|
||||
DMA_BEAT_SIZE_BYTE, // Bytes/hwords/words
|
||||
false, // Don't increment source address
|
||||
true); // Increment dest address
|
||||
firstWriteDescriptor = writeChannel.addDescriptor(
|
||||
NULL, // Source address (set later)
|
||||
(void *)getDataRegister(), // Dest (SPI data register)
|
||||
0, // Count (set later)
|
||||
DMA_BEAT_SIZE_BYTE, // Bytes/hwords/words
|
||||
true, // Increment source address
|
||||
false); // Don't increment dest address
|
||||
// This is the number of EXTRA descriptors beyond the first.
|
||||
int numReadDescriptors = ((maxReadBytes + 65534) / 65535) - 1;
|
||||
int numWriteDescriptors = ((maxWriteBytes + 65534) / 65535) - 1;
|
||||
int totalDescriptors = numReadDescriptors + numWriteDescriptors;
|
||||
|
||||
if(totalDescriptors <= 0) { // Don't need extra descriptors,
|
||||
use_dma = true; // channels are allocated, we're good.
|
||||
} else { // Else allocate extra descriptor lists...
|
||||
// Although DMA descriptors are technically a linked list, we just
|
||||
// allocate a chunk all at once, and finesse the pointers later.
|
||||
if((extraReadDescriptors = (DmacDescriptor *)malloc(
|
||||
totalDescriptors * sizeof(DmacDescriptor)))) {
|
||||
use_dma = true; // Everything allocated successfully
|
||||
extraWriteDescriptors = &extraReadDescriptors[numReadDescriptors];
|
||||
// Initialize descriptors (copy from first ones)
|
||||
for(int i=0; i<numReadDescriptors; i++) {
|
||||
memcpy(&extraReadDescriptors[i], firstReadDescriptor,
|
||||
sizeof(DmacDescriptor));
|
||||
}
|
||||
for(int i=0; i<numWriteDescriptors; i++) {
|
||||
memcpy(&extraWriteDescriptors[i], firstWriteDescriptor,
|
||||
sizeof(DmacDescriptor));
|
||||
}
|
||||
} // end malloc
|
||||
} // end extra descriptor check
|
||||
|
||||
if(use_dma) { // If everything allocated successfully,
|
||||
return; // then we're done here.
|
||||
} // Otherwise clean up interim allocations...
|
||||
writeChannel.free();
|
||||
} // end writeChannel alloc
|
||||
readChannel.free();
|
||||
} // end readChannel alloc
|
||||
|
||||
// NOT FATAL if channel or descriptor allocation fails.
|
||||
// transfer() function will fall back on a manual byte-by-byte loop.
|
||||
}
|
||||
|
||||
void SPIClass::transfer(const void *txbuf, void *rxbuf, size_t count,
|
||||
void SPIClass::transfer(const void* txbuf, void* rxbuf, size_t count,
|
||||
bool block) {
|
||||
|
||||
if((!txbuf && !rxbuf) || !count) { // Validate inputs
|
||||
return;
|
||||
}
|
||||
// OK to assume now that txbuf and/or rxbuf are non-NULL, an if/else is
|
||||
// often sufficient, don't need else-ifs for everything buffer related.
|
||||
|
||||
uint8_t *txbuf8 = (uint8_t *)txbuf; // Must cast to byte size
|
||||
uint8_t *rxbuf8 = (uint8_t *)rxbuf; // for pointer math
|
||||
|
||||
if(use_dma) { // DMA-BASED TRANSFER YAY ----------------------------------
|
||||
|
||||
static const uint8_t dum = 0xFF; // Dummy byte for read-only xfers
|
||||
|
||||
// Set up DMA descriptor lists -----------------------------------------
|
||||
|
||||
DmacDescriptor *rDesc = firstReadDescriptor;
|
||||
DmacDescriptor *wDesc = firstWriteDescriptor;
|
||||
int descIdx = 0; // Index into extra descriptor lists
|
||||
|
||||
while(count) { // Counts down to end of transfer
|
||||
uint32_t bytesThisDescriptor = count;
|
||||
if(bytesThisDescriptor > 65535) { // Limit each descriptor
|
||||
bytesThisDescriptor = 65535; // to 65535 (not 65536) bytes
|
||||
}
|
||||
rDesc->BTCNT.reg = wDesc->BTCNT.reg = bytesThisDescriptor;
|
||||
if(rxbuf) { // Read-only or read+write
|
||||
// Auto-inc addresses in DMA descriptors must point to END of data.
|
||||
// Buf pointers would advance at end of loop anyway, do it now...
|
||||
rxbuf8 += bytesThisDescriptor;
|
||||
rDesc->DSTADDR.reg = (uint32_t)rxbuf8;
|
||||
}
|
||||
if(txbuf) { // Write-only or read+write
|
||||
txbuf8 += bytesThisDescriptor; // Same as above
|
||||
wDesc->SRCADDR.reg = (uint32_t)txbuf8;
|
||||
wDesc->BTCTRL.bit.SRCINC = 1; // Increment source pointer
|
||||
} else { // Read-only requires dummy write
|
||||
wDesc->SRCADDR.reg = (uint32_t)&dum;
|
||||
wDesc->BTCTRL.bit.SRCINC = 0; // Don't increment source pointer
|
||||
}
|
||||
count -= bytesThisDescriptor;
|
||||
if(count) { // Still more data?
|
||||
// Link to next descriptors. Extra descriptors are IN ADDITION
|
||||
// to first, so it's safe and correct that descIdx starts at 0.
|
||||
rDesc->DESCADDR.reg = (uint32_t)&extraReadDescriptors[descIdx];
|
||||
wDesc->DESCADDR.reg = (uint32_t)&extraWriteDescriptors[descIdx];
|
||||
rDesc = &extraReadDescriptors[descIdx]; // Update pointers to
|
||||
wDesc = &extraWriteDescriptors[descIdx]; // next descriptors
|
||||
descIdx++;
|
||||
// A write-only transfer doesn't use the read descriptor list, but
|
||||
// it's quicker to build it (full of nonsense) anyway than to check.
|
||||
} else { // No more data, end descriptor linked lists
|
||||
rDesc->DESCADDR.reg = wDesc->DESCADDR.reg = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Set up DMA transfer job(s) ------------------------------------------
|
||||
|
||||
if(rxbuf) { // Read+write or read-only
|
||||
// End-of-read callback is already set up, disable write CB, start job
|
||||
writeChannel.setCallback(dmaDoNothingCallback);
|
||||
readChannel.startJob();
|
||||
} else { // Write-only, use end-of-write callback
|
||||
writeChannel.setCallback(dmaCallback);
|
||||
}
|
||||
|
||||
// Run DMA jobs, blocking if requested ---------------------------------
|
||||
|
||||
dma_busy = true;
|
||||
writeChannel.startJob(); // All xfers, even read-only, need write job.
|
||||
if(block) { // If blocking transfer requested,
|
||||
while(dma_busy); // wait for job to finish
|
||||
}
|
||||
|
||||
} else { // NON-DMA FALLBACK ---------------------------------------------
|
||||
|
||||
if(txbuf8) {
|
||||
if(rxbuf8) { // Write + read simultaneously
|
||||
while(count--) {
|
||||
*rxbuf8++ = _p_sercom->transferDataSPI(*txbuf8++);
|
||||
// If receiving data and the RX DMA channel is not yet allocated...
|
||||
if(rxbuf && (readChannel.getChannel() >= DMAC_CH_NUM)) {
|
||||
if(readChannel.allocate() == DMA_STATUS_OK) {
|
||||
readDescriptor =
|
||||
readChannel.addDescriptor(
|
||||
(void *)getDataRegister(), // Source address (SPI data reg)
|
||||
NULL, // Dest address (set later)
|
||||
0, // Count (set later)
|
||||
DMA_BEAT_SIZE_BYTE, // Bytes/hwords/words
|
||||
false, // Don't increment source address
|
||||
true); // Increment dest address
|
||||
readChannel.setTrigger(getDMAC_ID_RX());
|
||||
readChannel.setAction(DMA_TRIGGER_ACTON_BEAT);
|
||||
spiPtr[readChannel.getChannel()] = this;
|
||||
// Since all RX transfers involve a TX, a
|
||||
// separate callback here is not necessary.
|
||||
}
|
||||
} else { // Write only
|
||||
while(count--) {
|
||||
(void)_p_sercom->transferDataSPI(*txbuf8++);
|
||||
}
|
||||
}
|
||||
} else { // Read only
|
||||
while(count--) {
|
||||
*rxbuf8++ = _p_sercom->transferDataSPI(0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
} // end non-DMA
|
||||
// Unlike the rxbuf check above, where a RX DMA channel is allocated
|
||||
// only if receiving data (and channel not previously alloc'd), the
|
||||
// TX DMA channel is always needed, because even RX-only SPI requires
|
||||
// writing dummy bytes to the peripheral.
|
||||
if(writeChannel.getChannel() >= DMAC_CH_NUM) {
|
||||
if(writeChannel.allocate() == DMA_STATUS_OK) {
|
||||
writeDescriptor =
|
||||
writeChannel.addDescriptor(
|
||||
NULL, // Source address (set later)
|
||||
(void *)getDataRegister(), // Dest (SPI data register)
|
||||
0, // Count (set later)
|
||||
DMA_BEAT_SIZE_BYTE, // Bytes/hwords/words
|
||||
true, // Increment source address
|
||||
false); // Don't increment dest address
|
||||
writeChannel.setTrigger(getDMAC_ID_TX());
|
||||
writeChannel.setAction(DMA_TRIGGER_ACTON_BEAT);
|
||||
writeChannel.setCallback(dmaCallback);
|
||||
spiPtr[writeChannel.getChannel()] = this;
|
||||
}
|
||||
}
|
||||
|
||||
if(writeDescriptor && (readDescriptor || !rxbuf)) {
|
||||
static const uint8_t dum = 0xFF; // Dummy byte for read-only xfers
|
||||
|
||||
// Initialize read descriptor dest address to rxbuf
|
||||
if(rxbuf) readDescriptor->DSTADDR.reg = (uint32_t)rxbuf;
|
||||
|
||||
// If reading only, set up writeDescriptor to issue dummy bytes
|
||||
// (set SRCADDR to &dum and SRCINC to 0). Otherwise, set SRCADDR
|
||||
// to txbuf and SRCINC to 1. Only needed once at start.
|
||||
if(rxbuf && !txbuf) {
|
||||
writeDescriptor->SRCADDR.reg = (uint32_t)&dum;
|
||||
writeDescriptor->BTCTRL.bit.SRCINC = 0;
|
||||
} else {
|
||||
writeDescriptor->SRCADDR.reg = (uint32_t)txbuf;
|
||||
writeDescriptor->BTCTRL.bit.SRCINC = 1;
|
||||
}
|
||||
|
||||
while(count > 0) {
|
||||
// Maximum bytes per DMA descriptor is 65,535 (NOT 65,536).
|
||||
// We could set up a descriptor chain, but that gets more
|
||||
// complex. For now, instead, break up long transfers into
|
||||
// chunks of 65,535 bytes max...these transfers are all
|
||||
// blocking, regardless of the "block" argument, except
|
||||
// for the last one which will observe the background request.
|
||||
// The fractional part is done first, so for any "partially
|
||||
// blocking" transfers like these at least it's the largest
|
||||
// single-descriptor transfer possible that occurs in the
|
||||
// background, rather than the tail end.
|
||||
int bytesThisPass;
|
||||
bool blockThisPass;
|
||||
if(count > 65535) { // Too big for 1 descriptor
|
||||
blockThisPass = true;
|
||||
bytesThisPass = count % 65535; // Fractional part
|
||||
if(!bytesThisPass) bytesThisPass = 65535;
|
||||
} else {
|
||||
blockThisPass = block;
|
||||
bytesThisPass = count;
|
||||
}
|
||||
|
||||
// Issue 'bytesThisPass' bytes...
|
||||
if(rxbuf) {
|
||||
// Reading, or reading + writing.
|
||||
// Set up read descriptor.
|
||||
// Src address doesn't change, only dest & count.
|
||||
// DMA needs address set to END of buffer, so
|
||||
// increment the address now, before the transfer.
|
||||
readDescriptor->DSTADDR.reg += bytesThisPass;
|
||||
readDescriptor->BTCNT.reg = bytesThisPass;
|
||||
// Start the RX job BEFORE the TX job!
|
||||
// That's the whole secret sauce to the two-channel transfer.
|
||||
// Nothing will actually happen until the write channel job
|
||||
// is also started.
|
||||
readChannel.startJob();
|
||||
}
|
||||
if(txbuf) {
|
||||
// DMA needs address set to END of buffer, so
|
||||
// increment the address now, before the transfer.
|
||||
writeDescriptor->SRCADDR.reg += bytesThisPass;
|
||||
}
|
||||
writeDescriptor->BTCNT.reg = bytesThisPass;
|
||||
dma_busy = true;
|
||||
writeChannel.startJob();
|
||||
count -= bytesThisPass;
|
||||
if(blockThisPass) {
|
||||
while(dma_busy);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Non-DMA fallback.
|
||||
uint8_t *txbuf8 = (uint8_t *)txbuf,
|
||||
*rxbuf8 = (uint8_t *)rxbuf;
|
||||
if(rxbuf8) {
|
||||
if(txbuf8) {
|
||||
// Writing and reading simultaneously
|
||||
while(count--) {
|
||||
*rxbuf8++ = _p_sercom->transferDataSPI(*txbuf8++);
|
||||
}
|
||||
} else {
|
||||
// Reading only
|
||||
while(count--) {
|
||||
*rxbuf8++ = _p_sercom->transferDataSPI(0xFF);
|
||||
}
|
||||
}
|
||||
} else if(txbuf) {
|
||||
// Writing only
|
||||
while(count--) {
|
||||
(void)_p_sercom->transferDataSPI(*txbuf8++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Waits for a prior in-background DMA transfer to complete.
|
||||
void SPIClass::waitForTransfer(void) {
|
||||
while(dma_busy);
|
||||
while(dma_busy);
|
||||
}
|
||||
|
||||
// End DMA-based SPI transfer() code ---------------------------------------
|
||||
|
||||
void SPIClass::attachInterrupt() {
|
||||
// Should be enableInterrupt()
|
||||
}
|
||||
|
|
@ -479,12 +405,8 @@ static const struct {
|
|||
{ &SERCOM1->SPI.DATA.reg, SERCOM1_DMAC_ID_TX, SERCOM1_DMAC_ID_RX },
|
||||
{ &SERCOM2->SPI.DATA.reg, SERCOM2_DMAC_ID_TX, SERCOM2_DMAC_ID_RX },
|
||||
{ &SERCOM3->SPI.DATA.reg, SERCOM3_DMAC_ID_TX, SERCOM3_DMAC_ID_RX },
|
||||
#if defined(SERCOM4)
|
||||
{ &SERCOM4->SPI.DATA.reg, SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX },
|
||||
#endif
|
||||
#if defined(SERCOM5)
|
||||
{ &SERCOM5->SPI.DATA.reg, SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX },
|
||||
#endif
|
||||
#if defined(SERCOM6)
|
||||
{ &SERCOM6->SPI.DATA.reg, SERCOM6_DMAC_ID_TX, SERCOM6_DMAC_ID_RX },
|
||||
#endif
|
||||
|
|
@ -555,3 +477,4 @@ void SPIClass::setClockSource(SercomClockSource clk) {
|
|||
#if SPI_INTERFACES_COUNT > 5
|
||||
SPIClass SPI5(&PERIPH_SPI5, PIN_SPI5_MISO, PIN_SPI5_SCK, PIN_SPI5_MOSI, PAD_SPI5_TX, PAD_SPI5_RX);
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -52,11 +52,9 @@
|
|||
// The datasheet specifies a typical SPI SCK period (tSCK) of 42 ns,
|
||||
// see "Table 36-48. SPI Timing Characteristics and Requirements",
|
||||
// which translates into a maximum SPI clock of 23.8 MHz.
|
||||
// We'll permit use of 24 MHz SPI even though this is slightly out
|
||||
// of spec. Given how clock dividers work, the next "sensible"
|
||||
// threshold would be a substantial drop down to 12 MHz.
|
||||
// Conservatively, the divider is set for a 12 MHz maximum SPI clock.
|
||||
#if !defined(MAX_SPI)
|
||||
#define MAX_SPI 24000000
|
||||
#define MAX_SPI 12000000
|
||||
#endif
|
||||
#define SPI_MIN_CLOCK_DIVIDER (uint8_t)(1 + ((F_CPU - 1) / MAX_SPI))
|
||||
#endif
|
||||
|
|
@ -83,7 +81,7 @@ class SPISettings {
|
|||
#if defined(__SAMD51__)
|
||||
this->clockFreq = clock; // Clipping handled in SERCOM.cpp
|
||||
#else
|
||||
this->clockFreq = clock >= MAX_SPI ? MAX_SPI : clock;
|
||||
this->clockFreq = (clock >= (MAX_SPI * 2 / SPI_MIN_CLOCK_DIVIDER) ? MAX_SPI * 2 / SPI_MIN_CLOCK_DIVIDER : clock);
|
||||
#endif
|
||||
|
||||
this->bitOrder = (bitOrder == MSBFIRST ? MSB_FIRST : LSB_FIRST);
|
||||
|
|
@ -149,10 +147,11 @@ class SPIClass {
|
|||
#else
|
||||
// On SAMD21, this compiles to nothing, so user code doesn't need to
|
||||
// check and conditionally compile lines for different architectures.
|
||||
void setClockSource(SercomClockSource clk) { (void)clk; };
|
||||
void setClockSource(SercomClockSource clk) { };
|
||||
#endif // end __SAMD51__
|
||||
|
||||
private:
|
||||
void init();
|
||||
void config(SPISettings settings);
|
||||
|
||||
SERCOM *_p_sercom;
|
||||
|
|
@ -168,16 +167,12 @@ class SPIClass {
|
|||
char interruptSave;
|
||||
uint32_t interruptMask;
|
||||
|
||||
// transfer(txbuf, rxbuf, count, block) uses DMA when possible
|
||||
Adafruit_ZeroDMA readChannel;
|
||||
Adafruit_ZeroDMA writeChannel;
|
||||
DmacDescriptor *firstReadDescriptor = NULL; // List entry point
|
||||
DmacDescriptor *firstWriteDescriptor = NULL;
|
||||
DmacDescriptor *extraReadDescriptors = NULL; // Add'l descriptors
|
||||
DmacDescriptor *extraWriteDescriptors = NULL;
|
||||
bool use_dma = false; // true on successful alloc
|
||||
volatile bool dma_busy = false;
|
||||
void dmaAllocate(void);
|
||||
// transfer(txbuf, rxbuf, count, block) uses DMA if possible
|
||||
Adafruit_ZeroDMA readChannel,
|
||||
writeChannel;
|
||||
DmacDescriptor *readDescriptor = NULL,
|
||||
*writeDescriptor = NULL;
|
||||
volatile bool dma_busy = false;
|
||||
static void dmaCallback(Adafruit_ZeroDMA *dma);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
= Servo Library for Arduino =
|
||||
|
||||
This library allows an Arduino board to control RC (hobby) servo motors.
|
||||
|
||||
For more information about this library please visit us at
|
||||
http://www.arduino.cc/en/Reference/Servo
|
||||
|
||||
== License ==
|
||||
|
||||
Copyright (c) 2013 Arduino LLC. All right reserved.
|
||||
Copyright (c) 2009 Michael Margolis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
Controlling a servo position using a potentiometer (variable resistor)
|
||||
by Michal Rinott <http://people.interaction-ivrea.it/m.rinott>
|
||||
|
||||
modified on 8 Nov 2013
|
||||
by Scott Fitzgerald
|
||||
http://www.arduino.cc/en/Tutorial/Knob
|
||||
*/
|
||||
|
||||
#include <Servo.h>
|
||||
|
||||
Servo myservo; // create servo object to control a servo
|
||||
|
||||
int potpin = 0; // analog pin used to connect the potentiometer
|
||||
int val; // variable to read the value from the analog pin
|
||||
|
||||
void setup() {
|
||||
myservo.attach(9); // attaches the servo on pin 9 to the servo object
|
||||
}
|
||||
|
||||
void loop() {
|
||||
val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023)
|
||||
val = map(val, 0, 1023, 0, 180); // scale it to use it with the servo (value between 0 and 180)
|
||||
myservo.write(val); // sets the servo position according to the scaled value
|
||||
delay(15); // waits for the servo to get there
|
||||
}
|
||||
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/* Sweep
|
||||
by BARRAGAN <http://barraganstudio.com>
|
||||
This example code is in the public domain.
|
||||
|
||||
modified 8 Nov 2013
|
||||
by Scott Fitzgerald
|
||||
http://www.arduino.cc/en/Tutorial/Sweep
|
||||
*/
|
||||
|
||||
#include <Servo.h>
|
||||
|
||||
Servo myservo; // create servo object to control a servo
|
||||
// twelve servo objects can be created on most boards
|
||||
|
||||
int pos = 0; // variable to store the servo position
|
||||
|
||||
void setup() {
|
||||
myservo.attach(9); // attaches the servo on pin 9 to the servo object
|
||||
}
|
||||
|
||||
void loop() {
|
||||
for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
|
||||
// in steps of 1 degree
|
||||
myservo.write(pos); // tell servo to go to position in variable 'pos'
|
||||
delay(15); // waits 15ms for the servo to reach the position
|
||||
}
|
||||
for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
|
||||
myservo.write(pos); // tell servo to go to position in variable 'pos'
|
||||
delay(15); // waits 15ms for the servo to reach the position
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#######################################
|
||||
# Syntax Coloring Map Servo
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
Servo KEYWORD1 Servo
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
attach KEYWORD2
|
||||
detach KEYWORD2
|
||||
write KEYWORD2
|
||||
read KEYWORD2
|
||||
attached KEYWORD2
|
||||
writeMicroseconds KEYWORD2
|
||||
readMicroseconds KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
name=Servo
|
||||
version=1.1.4
|
||||
author=Michael Margolis, Arduino
|
||||
maintainer=Arduino <info@arduino.cc>
|
||||
sentence=Allows Arduino/Genuino boards to control a variety of servo motors.
|
||||
paragraph=This library can control a great number of servos.<br />It makes careful use of timers: the library can control 12 servos using only 1 timer.<br />On the Arduino Due you can control up to 60 servos.<br />
|
||||
category=Device Control
|
||||
url=http://www.arduino.cc/en/Reference/Servo
|
||||
architectures=avr,megaavr,sam,samd,nrf52,stm32f4
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
|
||||
Copyright (c) 2009 Michael Margolis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
A servo is activated by creating an instance of the Servo class passing
|
||||
the desired pin to the attach() method.
|
||||
The servos are pulsed in the background using the value most recently
|
||||
written using the write() method.
|
||||
|
||||
Note that analogWrite of PWM on pins associated with the timer are
|
||||
disabled when the first servo is attached.
|
||||
Timers are seized as needed in groups of 12 servos - 24 servos use two
|
||||
timers, 48 servos will use four.
|
||||
The sequence used to sieze timers is defined in timers.h
|
||||
|
||||
The methods are:
|
||||
|
||||
Servo - Class for manipulating servo motors connected to Arduino pins.
|
||||
|
||||
attach(pin ) - Attaches a servo motor to an i/o pin.
|
||||
attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds
|
||||
default min is 544, max is 2400
|
||||
|
||||
write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
|
||||
writeMicroseconds() - Sets the servo pulse width in microseconds
|
||||
read() - Gets the last written servo pulse width as an angle between 0 and 180.
|
||||
readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
|
||||
attached() - Returns true if there is a servo attached.
|
||||
detach() - Stops an attached servos from pulsing its i/o pin.
|
||||
*/
|
||||
|
||||
#ifndef Servo_h
|
||||
#define Servo_h
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/*
|
||||
* Defines for 16 bit timers used with Servo library
|
||||
*
|
||||
* If _useTimerX is defined then TimerX is a 16 bit timer on the current board
|
||||
* timer16_Sequence_t enumerates the sequence that the timers should be allocated
|
||||
* _Nbr_16timers indicates how many 16 bit timers are available.
|
||||
*/
|
||||
|
||||
// Architecture specific include
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
#include "avr/ServoTimers.h"
|
||||
#elif defined(ARDUINO_ARCH_SAM)
|
||||
#include "sam/ServoTimers.h"
|
||||
#elif defined(ARDUINO_ARCH_SAMD)
|
||||
#include "samd/ServoTimers.h"
|
||||
#elif defined(ARDUINO_ARCH_STM32F4)
|
||||
#include "stm32f4/ServoTimers.h"
|
||||
#elif defined(ARDUINO_ARCH_NRF52)
|
||||
#include "nrf52/ServoTimers.h"
|
||||
#elif defined(ARDUINO_ARCH_MEGAAVR)
|
||||
#include "megaavr/ServoTimers.h"
|
||||
#else
|
||||
#error "This library only supports boards with an AVR, SAM, SAMD, NRF52 or STM32F4 processor."
|
||||
#endif
|
||||
|
||||
#define Servo_VERSION 2 // software version of this library
|
||||
|
||||
#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
|
||||
#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
|
||||
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
|
||||
#define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds
|
||||
|
||||
#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer
|
||||
#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)
|
||||
|
||||
#define INVALID_SERVO 255 // flag indicating an invalid servo index
|
||||
|
||||
#if !defined(ARDUINO_ARCH_STM32F4)
|
||||
|
||||
typedef struct {
|
||||
uint8_t nbr :6 ; // a pin number from 0 to 63
|
||||
uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false
|
||||
} ServoPin_t ;
|
||||
|
||||
typedef struct {
|
||||
ServoPin_t Pin;
|
||||
volatile unsigned int ticks;
|
||||
} servo_t;
|
||||
|
||||
class Servo
|
||||
{
|
||||
public:
|
||||
Servo();
|
||||
uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
|
||||
uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes.
|
||||
void detach();
|
||||
void write(int value); // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds
|
||||
void writeMicroseconds(int value); // Write pulse width in microseconds
|
||||
int read(); // returns current pulse width as an angle between 0 and 180 degrees
|
||||
int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release)
|
||||
bool attached(); // return true if this servo is attached, otherwise false
|
||||
private:
|
||||
uint8_t servoIndex; // index into the channel data for this servo
|
||||
int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH
|
||||
int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -1,318 +0,0 @@
|
|||
/*
|
||||
Servo.cpp - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
|
||||
Copyright (c) 2009 Michael Margolis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
|
||||
#include <avr/interrupt.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "Servo.h"
|
||||
|
||||
#define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) // converts microseconds to tick (assumes prescale of 8) // 12 Aug 2009
|
||||
#define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds
|
||||
|
||||
|
||||
#define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays // 12 August 2009
|
||||
|
||||
//#define NBR_TIMERS (MAX_SERVOS / SERVOS_PER_TIMER)
|
||||
|
||||
static servo_t servos[MAX_SERVOS]; // static array of servo structures
|
||||
static volatile int8_t Channel[_Nbr_16timers ]; // counter for the servo being pulsed for each timer (or -1 if refresh interval)
|
||||
|
||||
uint8_t ServoCount = 0; // the total number of attached servos
|
||||
|
||||
|
||||
// convenience macros
|
||||
#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo
|
||||
#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer
|
||||
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
|
||||
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel
|
||||
|
||||
#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
|
||||
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo
|
||||
|
||||
/************ static functions common to all instances ***********************/
|
||||
|
||||
static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA)
|
||||
{
|
||||
if( Channel[timer] < 0 )
|
||||
*TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer
|
||||
else{
|
||||
if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true )
|
||||
digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated
|
||||
}
|
||||
|
||||
Channel[timer]++; // increment to the next channel
|
||||
if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
|
||||
*OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks;
|
||||
if(SERVO(timer,Channel[timer]).Pin.isActive == true) // check if activated
|
||||
digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high
|
||||
}
|
||||
else {
|
||||
// finished all channels so wait for the refresh period to expire before starting over
|
||||
if( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL) ) // allow a few ticks to ensure the next OCR1A not missed
|
||||
*OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL);
|
||||
else
|
||||
*OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed
|
||||
Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef WIRING // Wiring pre-defines signal handlers so don't define any if compiling for the Wiring platform
|
||||
// Interrupt handlers for Arduino
|
||||
#if defined(_useTimer1)
|
||||
SIGNAL (TIMER1_COMPA_vect)
|
||||
{
|
||||
handle_interrupts(_timer1, &TCNT1, &OCR1A);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_useTimer3)
|
||||
SIGNAL (TIMER3_COMPA_vect)
|
||||
{
|
||||
handle_interrupts(_timer3, &TCNT3, &OCR3A);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_useTimer4)
|
||||
SIGNAL (TIMER4_COMPA_vect)
|
||||
{
|
||||
handle_interrupts(_timer4, &TCNT4, &OCR4A);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_useTimer5)
|
||||
SIGNAL (TIMER5_COMPA_vect)
|
||||
{
|
||||
handle_interrupts(_timer5, &TCNT5, &OCR5A);
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined WIRING
|
||||
// Interrupt handlers for Wiring
|
||||
#if defined(_useTimer1)
|
||||
void Timer1Service()
|
||||
{
|
||||
handle_interrupts(_timer1, &TCNT1, &OCR1A);
|
||||
}
|
||||
#endif
|
||||
#if defined(_useTimer3)
|
||||
void Timer3Service()
|
||||
{
|
||||
handle_interrupts(_timer3, &TCNT3, &OCR3A);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
static void initISR(timer16_Sequence_t timer)
|
||||
{
|
||||
#if defined (_useTimer1)
|
||||
if(timer == _timer1) {
|
||||
TCCR1A = 0; // normal counting mode
|
||||
TCCR1B = _BV(CS11); // set prescaler of 8
|
||||
TCNT1 = 0; // clear the timer count
|
||||
#if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__)
|
||||
TIFR |= _BV(OCF1A); // clear any pending interrupts;
|
||||
TIMSK |= _BV(OCIE1A) ; // enable the output compare interrupt
|
||||
#else
|
||||
// here if not ATmega8 or ATmega128
|
||||
TIFR1 |= _BV(OCF1A); // clear any pending interrupts;
|
||||
TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt
|
||||
#endif
|
||||
#if defined(WIRING)
|
||||
timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (_useTimer3)
|
||||
if(timer == _timer3) {
|
||||
TCCR3A = 0; // normal counting mode
|
||||
TCCR3B = _BV(CS31); // set prescaler of 8
|
||||
TCNT3 = 0; // clear the timer count
|
||||
#if defined(__AVR_ATmega128__)
|
||||
TIFR |= _BV(OCF3A); // clear any pending interrupts;
|
||||
ETIMSK |= _BV(OCIE3A); // enable the output compare interrupt
|
||||
#else
|
||||
TIFR3 = _BV(OCF3A); // clear any pending interrupts;
|
||||
TIMSK3 = _BV(OCIE3A) ; // enable the output compare interrupt
|
||||
#endif
|
||||
#if defined(WIRING)
|
||||
timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (_useTimer4)
|
||||
if(timer == _timer4) {
|
||||
TCCR4A = 0; // normal counting mode
|
||||
TCCR4B = _BV(CS41); // set prescaler of 8
|
||||
TCNT4 = 0; // clear the timer count
|
||||
TIFR4 = _BV(OCF4A); // clear any pending interrupts;
|
||||
TIMSK4 = _BV(OCIE4A) ; // enable the output compare interrupt
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (_useTimer5)
|
||||
if(timer == _timer5) {
|
||||
TCCR5A = 0; // normal counting mode
|
||||
TCCR5B = _BV(CS51); // set prescaler of 8
|
||||
TCNT5 = 0; // clear the timer count
|
||||
TIFR5 = _BV(OCF5A); // clear any pending interrupts;
|
||||
TIMSK5 = _BV(OCIE5A) ; // enable the output compare interrupt
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void finISR(timer16_Sequence_t timer)
|
||||
{
|
||||
//disable use of the given timer
|
||||
#if defined WIRING // Wiring
|
||||
if(timer == _timer1) {
|
||||
#if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
|
||||
TIMSK1 &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt
|
||||
#else
|
||||
TIMSK &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt
|
||||
#endif
|
||||
timerDetach(TIMER1OUTCOMPAREA_INT);
|
||||
}
|
||||
else if(timer == _timer3) {
|
||||
#if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
|
||||
TIMSK3 &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt
|
||||
#else
|
||||
ETIMSK &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt
|
||||
#endif
|
||||
timerDetach(TIMER3OUTCOMPAREA_INT);
|
||||
}
|
||||
#else
|
||||
//For arduino - in future: call here to a currently undefined function to reset the timer
|
||||
(void) timer; // squash "unused parameter 'timer' [-Wunused-parameter]" warning
|
||||
#endif
|
||||
}
|
||||
|
||||
static boolean isTimerActive(timer16_Sequence_t timer)
|
||||
{
|
||||
// returns true if any servo is active on this timer
|
||||
for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) {
|
||||
if(SERVO(timer,channel).Pin.isActive == true)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/****************** end of static functions ******************************/
|
||||
|
||||
Servo::Servo()
|
||||
{
|
||||
if( ServoCount < MAX_SERVOS) {
|
||||
this->servoIndex = ServoCount++; // assign a servo index to this instance
|
||||
servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values - 12 Aug 2009
|
||||
}
|
||||
else
|
||||
this->servoIndex = INVALID_SERVO ; // too many servos
|
||||
}
|
||||
|
||||
uint8_t Servo::attach(int pin)
|
||||
{
|
||||
return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
|
||||
}
|
||||
|
||||
uint8_t Servo::attach(int pin, int min, int max)
|
||||
{
|
||||
if(this->servoIndex < MAX_SERVOS ) {
|
||||
pinMode( pin, OUTPUT) ; // set servo pin to output
|
||||
servos[this->servoIndex].Pin.nbr = pin;
|
||||
// todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
|
||||
this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
|
||||
this->max = (MAX_PULSE_WIDTH - max)/4;
|
||||
// initialize the timer if it has not already been initialized
|
||||
timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
|
||||
if(isTimerActive(timer) == false)
|
||||
initISR(timer);
|
||||
servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive
|
||||
}
|
||||
return this->servoIndex ;
|
||||
}
|
||||
|
||||
void Servo::detach()
|
||||
{
|
||||
servos[this->servoIndex].Pin.isActive = false;
|
||||
timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
|
||||
if(isTimerActive(timer) == false) {
|
||||
finISR(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void Servo::write(int value)
|
||||
{
|
||||
if(value < MIN_PULSE_WIDTH)
|
||||
{ // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
|
||||
if(value < 0) value = 0;
|
||||
if(value > 180) value = 180;
|
||||
value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
|
||||
}
|
||||
this->writeMicroseconds(value);
|
||||
}
|
||||
|
||||
void Servo::writeMicroseconds(int value)
|
||||
{
|
||||
// calculate and store the values for the given channel
|
||||
byte channel = this->servoIndex;
|
||||
if( (channel < MAX_SERVOS) ) // ensure channel is valid
|
||||
{
|
||||
if( value < SERVO_MIN() ) // ensure pulse width is valid
|
||||
value = SERVO_MIN();
|
||||
else if( value > SERVO_MAX() )
|
||||
value = SERVO_MAX();
|
||||
|
||||
value = value - TRIM_DURATION;
|
||||
value = usToTicks(value); // convert to ticks after compensating for interrupt overhead - 12 Aug 2009
|
||||
|
||||
uint8_t oldSREG = SREG;
|
||||
cli();
|
||||
servos[channel].ticks = value;
|
||||
SREG = oldSREG;
|
||||
}
|
||||
}
|
||||
|
||||
int Servo::read() // return the value as degrees
|
||||
{
|
||||
return map( this->readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
|
||||
}
|
||||
|
||||
int Servo::readMicroseconds()
|
||||
{
|
||||
unsigned int pulsewidth;
|
||||
if( this->servoIndex != INVALID_SERVO )
|
||||
pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION ; // 12 aug 2009
|
||||
else
|
||||
pulsewidth = 0;
|
||||
|
||||
return pulsewidth;
|
||||
}
|
||||
|
||||
bool Servo::attached()
|
||||
{
|
||||
return servos[this->servoIndex].Pin.isActive ;
|
||||
}
|
||||
|
||||
#endif // ARDUINO_ARCH_AVR
|
||||
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
|
||||
Copyright (c) 2009 Michael Margolis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Defines for 16 bit timers used with Servo library
|
||||
*
|
||||
* If _useTimerX is defined then TimerX is a 16 bit timer on the current board
|
||||
* timer16_Sequence_t enumerates the sequence that the timers should be allocated
|
||||
* _Nbr_16timers indicates how many 16 bit timers are available.
|
||||
*/
|
||||
|
||||
/**
|
||||
* AVR Only definitions
|
||||
* --------------------
|
||||
*/
|
||||
|
||||
// Say which 16 bit timers can be used and in what order
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
#define _useTimer5
|
||||
#define _useTimer1
|
||||
#define _useTimer3
|
||||
#define _useTimer4
|
||||
typedef enum { _timer5, _timer1, _timer3, _timer4, _Nbr_16timers } timer16_Sequence_t;
|
||||
|
||||
#elif defined(__AVR_ATmega32U4__)
|
||||
#define _useTimer1
|
||||
typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t;
|
||||
|
||||
#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
|
||||
#define _useTimer3
|
||||
#define _useTimer1
|
||||
typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t;
|
||||
|
||||
#elif defined(__AVR_ATmega128__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega2561__)
|
||||
#define _useTimer3
|
||||
#define _useTimer1
|
||||
typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t;
|
||||
|
||||
#else // everything else
|
||||
#define _useTimer1
|
||||
typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t;
|
||||
#endif
|
||||
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
#if defined(ARDUINO_ARCH_MEGAAVR)
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Servo.h>
|
||||
|
||||
#define usToTicks(_us) ((clockCyclesPerMicrosecond() / 16 * _us) / 4) // converts microseconds to tick
|
||||
#define ticksToUs(_ticks) (((unsigned) _ticks * 16) / (clockCyclesPerMicrosecond() / 4)) // converts from ticks back to microseconds
|
||||
|
||||
#define TRIM_DURATION 5 // compensation ticks to trim adjust for digitalWrite delays
|
||||
|
||||
static servo_t servos[MAX_SERVOS]; // static array of servo structures
|
||||
|
||||
uint8_t ServoCount = 0; // the total number of attached servos
|
||||
|
||||
static volatile int8_t currentServoIndex[_Nbr_16timers]; // index for the servo being pulsed for each timer (or -1 if refresh interval)
|
||||
|
||||
// convenience macros
|
||||
#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo
|
||||
#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer
|
||||
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
|
||||
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel
|
||||
|
||||
#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
|
||||
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo
|
||||
|
||||
void ServoHandler(int timer)
|
||||
{
|
||||
if (currentServoIndex[timer] < 0) {
|
||||
// Write compare register
|
||||
_timer->CCMP = 0;
|
||||
} else {
|
||||
if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && SERVO(timer, currentServoIndex[timer]).Pin.isActive == true) {
|
||||
digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, LOW); // pulse this channel low if activated
|
||||
}
|
||||
}
|
||||
|
||||
// Select the next servo controlled by this timer
|
||||
currentServoIndex[timer]++;
|
||||
|
||||
if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && currentServoIndex[timer] < SERVOS_PER_TIMER) {
|
||||
if (SERVO(timer, currentServoIndex[timer]).Pin.isActive == true) { // check if activated
|
||||
digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, HIGH); // it's an active channel so pulse it high
|
||||
}
|
||||
|
||||
// Get the counter value
|
||||
uint16_t tcCounterValue = 0; //_timer->CCMP;
|
||||
_timer->CCMP = (uint16_t) (tcCounterValue + SERVO(timer, currentServoIndex[timer]).ticks);
|
||||
}
|
||||
else {
|
||||
// finished all channels so wait for the refresh period to expire before starting over
|
||||
|
||||
// Get the counter value
|
||||
uint16_t tcCounterValue = _timer->CCMP;
|
||||
|
||||
if (tcCounterValue + 4UL < usToTicks(REFRESH_INTERVAL)) { // allow a few ticks to ensure the next OCR1A not missed
|
||||
_timer->CCMP = (uint16_t) usToTicks(REFRESH_INTERVAL);
|
||||
}
|
||||
else {
|
||||
_timer->CCMP = (uint16_t) (tcCounterValue + 4UL); // at least REFRESH_INTERVAL has elapsed
|
||||
}
|
||||
|
||||
currentServoIndex[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
|
||||
}
|
||||
|
||||
/* Clear flag */
|
||||
_timer->INTFLAGS = TCB_CAPT_bm;
|
||||
}
|
||||
|
||||
#if defined USE_TIMERB0
|
||||
ISR(TCB0_INT_vect)
|
||||
#elif defined USE_TIMERB1
|
||||
ISR(TCB1_INT_vect)
|
||||
#elif defined USE_TIMERB2
|
||||
ISR(TCB2_INT_vect)
|
||||
#endif
|
||||
{
|
||||
ServoHandler(0);
|
||||
}
|
||||
|
||||
static void initISR(timer16_Sequence_t timer)
|
||||
{
|
||||
//TCA0.SINGLE.CTRLA = (TCA_SINGLE_CLKSEL_DIV16_gc) | (TCA_SINGLE_ENABLE_bm);
|
||||
|
||||
_timer->CTRLA = TCB_CLKSEL_CLKTCA_gc;
|
||||
// Timer to Periodic interrupt mode
|
||||
// This write will also disable any active PWM outputs
|
||||
_timer->CTRLB = TCB_CNTMODE_INT_gc;
|
||||
// Enable interrupt
|
||||
_timer->INTCTRL = TCB_CAPTEI_bm;
|
||||
// Enable timer
|
||||
_timer->CTRLA |= TCB_ENABLE_bm;
|
||||
}
|
||||
|
||||
static void finISR(timer16_Sequence_t timer)
|
||||
{
|
||||
// Disable interrupt
|
||||
_timer->INTCTRL = 0;
|
||||
}
|
||||
|
||||
static boolean isTimerActive(timer16_Sequence_t timer)
|
||||
{
|
||||
// returns true if any servo is active on this timer
|
||||
for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) {
|
||||
if(SERVO(timer,channel).Pin.isActive == true)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/****************** end of static functions ******************************/
|
||||
|
||||
Servo::Servo()
|
||||
{
|
||||
if (ServoCount < MAX_SERVOS) {
|
||||
this->servoIndex = ServoCount++; // assign a servo index to this instance
|
||||
servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values
|
||||
} else {
|
||||
this->servoIndex = INVALID_SERVO; // too many servos
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Servo::attach(int pin)
|
||||
{
|
||||
return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
|
||||
}
|
||||
|
||||
uint8_t Servo::attach(int pin, int min, int max)
|
||||
{
|
||||
timer16_Sequence_t timer;
|
||||
|
||||
if (this->servoIndex < MAX_SERVOS) {
|
||||
pinMode(pin, OUTPUT); // set servo pin to output
|
||||
servos[this->servoIndex].Pin.nbr = pin;
|
||||
// todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
|
||||
this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
|
||||
this->max = (MAX_PULSE_WIDTH - max)/4;
|
||||
// initialize the timer if it has not already been initialized
|
||||
timer = SERVO_INDEX_TO_TIMER(servoIndex);
|
||||
if (isTimerActive(timer) == false) {
|
||||
initISR(timer);
|
||||
}
|
||||
servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive
|
||||
}
|
||||
return this->servoIndex;
|
||||
}
|
||||
|
||||
void Servo::detach()
|
||||
{
|
||||
timer16_Sequence_t timer;
|
||||
|
||||
servos[this->servoIndex].Pin.isActive = false;
|
||||
timer = SERVO_INDEX_TO_TIMER(servoIndex);
|
||||
if(isTimerActive(timer) == false) {
|
||||
finISR(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void Servo::write(int value)
|
||||
{
|
||||
// treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
|
||||
if (value < MIN_PULSE_WIDTH)
|
||||
{
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
else if (value > 180)
|
||||
value = 180;
|
||||
|
||||
value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
|
||||
}
|
||||
writeMicroseconds(value);
|
||||
}
|
||||
|
||||
void Servo::writeMicroseconds(int value)
|
||||
{
|
||||
// calculate and store the values for the given channel
|
||||
byte channel = this->servoIndex;
|
||||
if( (channel < MAX_SERVOS) ) // ensure channel is valid
|
||||
{
|
||||
if (value < SERVO_MIN()) // ensure pulse width is valid
|
||||
value = SERVO_MIN();
|
||||
else if (value > SERVO_MAX())
|
||||
value = SERVO_MAX();
|
||||
|
||||
value = value - TRIM_DURATION;
|
||||
value = usToTicks(value); // convert to ticks after compensating for interrupt overhead
|
||||
servos[channel].ticks = value;
|
||||
}
|
||||
}
|
||||
|
||||
int Servo::read() // return the value as degrees
|
||||
{
|
||||
return map(readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
|
||||
}
|
||||
|
||||
int Servo::readMicroseconds()
|
||||
{
|
||||
unsigned int pulsewidth;
|
||||
if (this->servoIndex != INVALID_SERVO)
|
||||
pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION;
|
||||
else
|
||||
pulsewidth = 0;
|
||||
|
||||
return pulsewidth;
|
||||
}
|
||||
|
||||
bool Servo::attached()
|
||||
{
|
||||
return servos[this->servoIndex].Pin.isActive;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Defines for 16 bit timers used with Servo library
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SERVO_TIMERS_H__
|
||||
#define __SERVO_TIMERS_H__
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
#define USE_TIMERB1 // interferes with PWM on pin 3
|
||||
//#define USE_TIMERB2 // interferes with PWM on pin 11
|
||||
//#define USE_TIMERB0 // interferes with PWM on pin 6
|
||||
|
||||
#if !defined(USE_TIMERB1) && !defined(USE_TIMERB2) && !defined(USE_TIMERB0)
|
||||
# error "No timers allowed for Servo"
|
||||
/* Please uncomment a timer above and rebuild */
|
||||
#endif
|
||||
|
||||
static volatile TCB_t* _timer =
|
||||
#if defined(USE_TIMERB0)
|
||||
&TCB0;
|
||||
#endif
|
||||
#if defined(USE_TIMERB1)
|
||||
&TCB1;
|
||||
#endif
|
||||
#if defined(USE_TIMERB2)
|
||||
&TCB2;
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
timer0,
|
||||
_Nbr_16timers } timer16_Sequence_t;
|
||||
|
||||
|
||||
#endif /* __SERVO_TIMERS_H__ */
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2016 Arduino. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if defined(ARDUINO_ARCH_NRF52)
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Servo.h>
|
||||
|
||||
|
||||
static servo_t servos[MAX_SERVOS]; // static array of servo structures
|
||||
|
||||
uint8_t ServoCount = 0; // the total number of attached servos
|
||||
|
||||
|
||||
|
||||
uint32_t group_pins[3][NRF_PWM_CHANNEL_COUNT]={{NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED}, {NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED}, {NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED}};
|
||||
static uint16_t seq_values[3][NRF_PWM_CHANNEL_COUNT]={{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
|
||||
|
||||
Servo::Servo()
|
||||
{
|
||||
if (ServoCount < MAX_SERVOS) {
|
||||
this->servoIndex = ServoCount++; // assign a servo index to this instance
|
||||
} else {
|
||||
this->servoIndex = INVALID_SERVO; // too many servos
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8_t Servo::attach(int pin)
|
||||
{
|
||||
|
||||
return this->attach(pin, 0, 2500);
|
||||
}
|
||||
|
||||
|
||||
uint8_t Servo::attach(int pin, int min, int max)
|
||||
{
|
||||
int servo_min, servo_max;
|
||||
if (this->servoIndex < MAX_SERVOS) {
|
||||
pinMode(pin, OUTPUT); // set servo pin to output
|
||||
servos[this->servoIndex].Pin.nbr = pin;
|
||||
|
||||
if(min < servo_min) min = servo_min;
|
||||
if (max > servo_max) max = servo_max;
|
||||
this->min = min;
|
||||
this->max = max;
|
||||
|
||||
servos[this->servoIndex].Pin.isActive = true;
|
||||
|
||||
}
|
||||
return this->servoIndex;
|
||||
}
|
||||
|
||||
void Servo::detach()
|
||||
{
|
||||
servos[this->servoIndex].Pin.isActive = false;
|
||||
}
|
||||
|
||||
|
||||
void Servo::write(int value)
|
||||
{
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
else if (value > 180)
|
||||
value = 180;
|
||||
value = map(value, 0, 180, MIN_PULSE, MAX_PULSE);
|
||||
|
||||
writeMicroseconds(value);
|
||||
}
|
||||
|
||||
|
||||
void Servo::writeMicroseconds(int value)
|
||||
{
|
||||
uint8_t channel, instance;
|
||||
uint8_t pin = servos[this->servoIndex].Pin.nbr;
|
||||
//instance of pwm module is MSB - look at VWariant.h
|
||||
instance=(g_APinDescription[pin].ulPWMChannel & 0xF0)/16;
|
||||
//index of pwm channel is LSB - look at VWariant.h
|
||||
channel=g_APinDescription[pin].ulPWMChannel & 0x0F;
|
||||
group_pins[instance][channel]=g_APinDescription[pin].ulPin;
|
||||
NRF_PWM_Type * PWMInstance = instance == 0 ? NRF_PWM0 : (instance == 1 ? NRF_PWM1 : NRF_PWM2);
|
||||
//configure pwm instance and enable it
|
||||
seq_values[instance][channel]= value | 0x8000;
|
||||
nrf_pwm_sequence_t const seq={
|
||||
seq_values[instance],
|
||||
NRF_PWM_VALUES_LENGTH(seq_values),
|
||||
0,
|
||||
0
|
||||
};
|
||||
nrf_pwm_pins_set(PWMInstance, group_pins[instance]);
|
||||
nrf_pwm_enable(PWMInstance);
|
||||
nrf_pwm_configure(PWMInstance, NRF_PWM_CLK_125kHz, NRF_PWM_MODE_UP, 2500); // 20ms - 50Hz
|
||||
nrf_pwm_decoder_set(PWMInstance, NRF_PWM_LOAD_INDIVIDUAL, NRF_PWM_STEP_AUTO);
|
||||
nrf_pwm_sequence_set(PWMInstance, 0, &seq);
|
||||
nrf_pwm_loop_set(PWMInstance, 0UL);
|
||||
nrf_pwm_task_trigger(PWMInstance, NRF_PWM_TASK_SEQSTART0);
|
||||
}
|
||||
|
||||
int Servo::read() // return the value as degrees
|
||||
{
|
||||
return map(readMicroseconds(), MIN_PULSE, MAX_PULSE, 0, 180);
|
||||
}
|
||||
|
||||
int Servo::readMicroseconds()
|
||||
{
|
||||
uint8_t channel, instance;
|
||||
uint8_t pin=servos[this->servoIndex].Pin.nbr;
|
||||
instance=(g_APinDescription[pin].ulPWMChannel & 0xF0)/16;
|
||||
channel=g_APinDescription[pin].ulPWMChannel & 0x0F;
|
||||
// remove the 16th bit we added before
|
||||
return seq_values[instance][channel] & 0x7FFF;
|
||||
}
|
||||
|
||||
bool Servo::attached()
|
||||
{
|
||||
return servos[this->servoIndex].Pin.isActive;
|
||||
}
|
||||
|
||||
#endif // ARDUINO_ARCH_NRF52
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2016 Arduino. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* NRF52 doesn't use timer, but pwm. This file include definitions to keep
|
||||
* compatibility with the Servo library standards.
|
||||
*/
|
||||
|
||||
#ifndef __SERVO_TIMERS_H__
|
||||
#define __SERVO_TIMERS_H__
|
||||
|
||||
/**
|
||||
* NRF52 Only definitions
|
||||
* ---------------------
|
||||
*/
|
||||
|
||||
#define MIN_PULSE 55
|
||||
#define MAX_PULSE 284
|
||||
|
||||
// define one timer in order to have MAX_SERVOS = 12
|
||||
typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t;
|
||||
|
||||
#endif // __SERVO_TIMERS_H__
|
||||
|
|
@ -1,283 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2013 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if defined(ARDUINO_ARCH_SAM)
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Servo.h>
|
||||
|
||||
#define usToTicks(_us) (( clockCyclesPerMicrosecond() * _us) / 32) // converts microseconds to tick
|
||||
#define ticksToUs(_ticks) (( (unsigned)_ticks * 32)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds
|
||||
|
||||
#define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays
|
||||
|
||||
static servo_t servos[MAX_SERVOS]; // static array of servo structures
|
||||
|
||||
uint8_t ServoCount = 0; // the total number of attached servos
|
||||
|
||||
static volatile int8_t Channel[_Nbr_16timers ]; // counter for the servo being pulsed for each timer (or -1 if refresh interval)
|
||||
|
||||
// convenience macros
|
||||
#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo
|
||||
#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer
|
||||
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
|
||||
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel
|
||||
|
||||
#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
|
||||
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo
|
||||
|
||||
/************ static functions common to all instances ***********************/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// Interrupt handler for the TC0 channel 1.
|
||||
//------------------------------------------------------------------------------
|
||||
void Servo_Handler(timer16_Sequence_t timer, Tc *pTc, uint8_t channel);
|
||||
#if defined (_useTimer1)
|
||||
void HANDLER_FOR_TIMER1(void) {
|
||||
Servo_Handler(_timer1, TC_FOR_TIMER1, CHANNEL_FOR_TIMER1);
|
||||
}
|
||||
#endif
|
||||
#if defined (_useTimer2)
|
||||
void HANDLER_FOR_TIMER2(void) {
|
||||
Servo_Handler(_timer2, TC_FOR_TIMER2, CHANNEL_FOR_TIMER2);
|
||||
}
|
||||
#endif
|
||||
#if defined (_useTimer3)
|
||||
void HANDLER_FOR_TIMER3(void) {
|
||||
Servo_Handler(_timer3, TC_FOR_TIMER3, CHANNEL_FOR_TIMER3);
|
||||
}
|
||||
#endif
|
||||
#if defined (_useTimer4)
|
||||
void HANDLER_FOR_TIMER4(void) {
|
||||
Servo_Handler(_timer4, TC_FOR_TIMER4, CHANNEL_FOR_TIMER4);
|
||||
}
|
||||
#endif
|
||||
#if defined (_useTimer5)
|
||||
void HANDLER_FOR_TIMER5(void) {
|
||||
Servo_Handler(_timer5, TC_FOR_TIMER5, CHANNEL_FOR_TIMER5);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Servo_Handler(timer16_Sequence_t timer, Tc *tc, uint8_t channel)
|
||||
{
|
||||
// clear interrupt
|
||||
tc->TC_CHANNEL[channel].TC_SR;
|
||||
if (Channel[timer] < 0) {
|
||||
tc->TC_CHANNEL[channel].TC_CCR |= TC_CCR_SWTRG; // channel set to -1 indicated that refresh interval completed so reset the timer
|
||||
} else {
|
||||
if (SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true) {
|
||||
digitalWrite(SERVO(timer,Channel[timer]).Pin.nbr, LOW); // pulse this channel low if activated
|
||||
}
|
||||
}
|
||||
|
||||
Channel[timer]++; // increment to the next channel
|
||||
if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
|
||||
tc->TC_CHANNEL[channel].TC_RA = tc->TC_CHANNEL[channel].TC_CV + SERVO(timer,Channel[timer]).ticks;
|
||||
if(SERVO(timer,Channel[timer]).Pin.isActive == true) { // check if activated
|
||||
digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high
|
||||
}
|
||||
}
|
||||
else {
|
||||
// finished all channels so wait for the refresh period to expire before starting over
|
||||
if( (tc->TC_CHANNEL[channel].TC_CV) + 4 < usToTicks(REFRESH_INTERVAL) ) { // allow a few ticks to ensure the next OCR1A not missed
|
||||
tc->TC_CHANNEL[channel].TC_RA = (unsigned int)usToTicks(REFRESH_INTERVAL);
|
||||
}
|
||||
else {
|
||||
tc->TC_CHANNEL[channel].TC_RA = tc->TC_CHANNEL[channel].TC_CV + 4; // at least REFRESH_INTERVAL has elapsed
|
||||
}
|
||||
Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
|
||||
}
|
||||
}
|
||||
|
||||
static void _initISR(Tc *tc, uint32_t channel, uint32_t id, IRQn_Type irqn)
|
||||
{
|
||||
pmc_enable_periph_clk(id);
|
||||
TC_Configure(tc, channel,
|
||||
TC_CMR_TCCLKS_TIMER_CLOCK3 | // MCK/32
|
||||
TC_CMR_WAVE | // Waveform mode
|
||||
TC_CMR_WAVSEL_UP_RC ); // Counter running up and reset when equals to RC
|
||||
|
||||
/* 84MHz, MCK/32, for 1.5ms: 3937 */
|
||||
TC_SetRA(tc, channel, 2625); // 1ms
|
||||
|
||||
/* Configure and enable interrupt */
|
||||
NVIC_EnableIRQ(irqn);
|
||||
// TC_IER_CPAS: RA Compare
|
||||
tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPAS;
|
||||
|
||||
// Enables the timer clock and performs a software reset to start the counting
|
||||
TC_Start(tc, channel);
|
||||
}
|
||||
|
||||
static void initISR(timer16_Sequence_t timer)
|
||||
{
|
||||
#if defined (_useTimer1)
|
||||
if (timer == _timer1)
|
||||
_initISR(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, ID_TC_FOR_TIMER1, IRQn_FOR_TIMER1);
|
||||
#endif
|
||||
#if defined (_useTimer2)
|
||||
if (timer == _timer2)
|
||||
_initISR(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, ID_TC_FOR_TIMER2, IRQn_FOR_TIMER2);
|
||||
#endif
|
||||
#if defined (_useTimer3)
|
||||
if (timer == _timer3)
|
||||
_initISR(TC_FOR_TIMER3, CHANNEL_FOR_TIMER3, ID_TC_FOR_TIMER3, IRQn_FOR_TIMER3);
|
||||
#endif
|
||||
#if defined (_useTimer4)
|
||||
if (timer == _timer4)
|
||||
_initISR(TC_FOR_TIMER4, CHANNEL_FOR_TIMER4, ID_TC_FOR_TIMER4, IRQn_FOR_TIMER4);
|
||||
#endif
|
||||
#if defined (_useTimer5)
|
||||
if (timer == _timer5)
|
||||
_initISR(TC_FOR_TIMER5, CHANNEL_FOR_TIMER5, ID_TC_FOR_TIMER5, IRQn_FOR_TIMER5);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void finISR(timer16_Sequence_t timer)
|
||||
{
|
||||
#if defined (_useTimer1)
|
||||
TC_Stop(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1);
|
||||
#endif
|
||||
#if defined (_useTimer2)
|
||||
TC_Stop(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2);
|
||||
#endif
|
||||
#if defined (_useTimer3)
|
||||
TC_Stop(TC_FOR_TIMER3, CHANNEL_FOR_TIMER3);
|
||||
#endif
|
||||
#if defined (_useTimer4)
|
||||
TC_Stop(TC_FOR_TIMER4, CHANNEL_FOR_TIMER4);
|
||||
#endif
|
||||
#if defined (_useTimer5)
|
||||
TC_Stop(TC_FOR_TIMER5, CHANNEL_FOR_TIMER5);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static boolean isTimerActive(timer16_Sequence_t timer)
|
||||
{
|
||||
// returns true if any servo is active on this timer
|
||||
for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) {
|
||||
if(SERVO(timer,channel).Pin.isActive == true)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/****************** end of static functions ******************************/
|
||||
|
||||
Servo::Servo()
|
||||
{
|
||||
if (ServoCount < MAX_SERVOS) {
|
||||
this->servoIndex = ServoCount++; // assign a servo index to this instance
|
||||
servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values
|
||||
} else {
|
||||
this->servoIndex = INVALID_SERVO; // too many servos
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Servo::attach(int pin)
|
||||
{
|
||||
return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
|
||||
}
|
||||
|
||||
uint8_t Servo::attach(int pin, int min, int max)
|
||||
{
|
||||
timer16_Sequence_t timer;
|
||||
|
||||
if (this->servoIndex < MAX_SERVOS) {
|
||||
pinMode(pin, OUTPUT); // set servo pin to output
|
||||
servos[this->servoIndex].Pin.nbr = pin;
|
||||
// todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
|
||||
this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
|
||||
this->max = (MAX_PULSE_WIDTH - max)/4;
|
||||
// initialize the timer if it has not already been initialized
|
||||
timer = SERVO_INDEX_TO_TIMER(servoIndex);
|
||||
if (isTimerActive(timer) == false) {
|
||||
initISR(timer);
|
||||
}
|
||||
servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive
|
||||
}
|
||||
return this->servoIndex;
|
||||
}
|
||||
|
||||
void Servo::detach()
|
||||
{
|
||||
timer16_Sequence_t timer;
|
||||
|
||||
servos[this->servoIndex].Pin.isActive = false;
|
||||
timer = SERVO_INDEX_TO_TIMER(servoIndex);
|
||||
if(isTimerActive(timer) == false) {
|
||||
finISR(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void Servo::write(int value)
|
||||
{
|
||||
// treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
|
||||
if (value < MIN_PULSE_WIDTH)
|
||||
{
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
else if (value > 180)
|
||||
value = 180;
|
||||
|
||||
value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
|
||||
}
|
||||
writeMicroseconds(value);
|
||||
}
|
||||
|
||||
void Servo::writeMicroseconds(int value)
|
||||
{
|
||||
// calculate and store the values for the given channel
|
||||
byte channel = this->servoIndex;
|
||||
if( (channel < MAX_SERVOS) ) // ensure channel is valid
|
||||
{
|
||||
if (value < SERVO_MIN()) // ensure pulse width is valid
|
||||
value = SERVO_MIN();
|
||||
else if (value > SERVO_MAX())
|
||||
value = SERVO_MAX();
|
||||
|
||||
value = value - TRIM_DURATION;
|
||||
value = usToTicks(value); // convert to ticks after compensating for interrupt overhead
|
||||
servos[channel].ticks = value;
|
||||
}
|
||||
}
|
||||
|
||||
int Servo::read() // return the value as degrees
|
||||
{
|
||||
return map(readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
|
||||
}
|
||||
|
||||
int Servo::readMicroseconds()
|
||||
{
|
||||
unsigned int pulsewidth;
|
||||
if (this->servoIndex != INVALID_SERVO)
|
||||
pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION;
|
||||
else
|
||||
pulsewidth = 0;
|
||||
|
||||
return pulsewidth;
|
||||
}
|
||||
|
||||
bool Servo::attached()
|
||||
{
|
||||
return servos[this->servoIndex].Pin.isActive;
|
||||
}
|
||||
|
||||
#endif // ARDUINO_ARCH_SAM
|
||||
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2013 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Defines for 16 bit timers used with Servo library
|
||||
*
|
||||
* If _useTimerX is defined then TimerX is a 16 bit timer on the current board
|
||||
* timer16_Sequence_t enumerates the sequence that the timers should be allocated
|
||||
* _Nbr_16timers indicates how many 16 bit timers are available.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SAM Only definitions
|
||||
* --------------------
|
||||
*/
|
||||
|
||||
// For SAM3X:
|
||||
#define _useTimer1
|
||||
#define _useTimer2
|
||||
#define _useTimer3
|
||||
#define _useTimer4
|
||||
#define _useTimer5
|
||||
|
||||
/*
|
||||
TC0, chan 0 => TC0_Handler
|
||||
TC0, chan 1 => TC1_Handler
|
||||
TC0, chan 2 => TC2_Handler
|
||||
TC1, chan 0 => TC3_Handler
|
||||
TC1, chan 1 => TC4_Handler
|
||||
TC1, chan 2 => TC5_Handler
|
||||
TC2, chan 0 => TC6_Handler
|
||||
TC2, chan 1 => TC7_Handler
|
||||
TC2, chan 2 => TC8_Handler
|
||||
*/
|
||||
|
||||
#if defined (_useTimer1)
|
||||
#define TC_FOR_TIMER1 TC1
|
||||
#define CHANNEL_FOR_TIMER1 0
|
||||
#define ID_TC_FOR_TIMER1 ID_TC3
|
||||
#define IRQn_FOR_TIMER1 TC3_IRQn
|
||||
#define HANDLER_FOR_TIMER1 TC3_Handler
|
||||
#endif
|
||||
#if defined (_useTimer2)
|
||||
#define TC_FOR_TIMER2 TC1
|
||||
#define CHANNEL_FOR_TIMER2 1
|
||||
#define ID_TC_FOR_TIMER2 ID_TC4
|
||||
#define IRQn_FOR_TIMER2 TC4_IRQn
|
||||
#define HANDLER_FOR_TIMER2 TC4_Handler
|
||||
#endif
|
||||
#if defined (_useTimer3)
|
||||
#define TC_FOR_TIMER3 TC1
|
||||
#define CHANNEL_FOR_TIMER3 2
|
||||
#define ID_TC_FOR_TIMER3 ID_TC5
|
||||
#define IRQn_FOR_TIMER3 TC5_IRQn
|
||||
#define HANDLER_FOR_TIMER3 TC5_Handler
|
||||
#endif
|
||||
#if defined (_useTimer4)
|
||||
#define TC_FOR_TIMER4 TC0
|
||||
#define CHANNEL_FOR_TIMER4 2
|
||||
#define ID_TC_FOR_TIMER4 ID_TC2
|
||||
#define IRQn_FOR_TIMER4 TC2_IRQn
|
||||
#define HANDLER_FOR_TIMER4 TC2_Handler
|
||||
#endif
|
||||
#if defined (_useTimer5)
|
||||
#define TC_FOR_TIMER5 TC0
|
||||
#define CHANNEL_FOR_TIMER5 0
|
||||
#define ID_TC_FOR_TIMER5 ID_TC0
|
||||
#define IRQn_FOR_TIMER5 TC0_IRQn
|
||||
#define HANDLER_FOR_TIMER5 TC0_Handler
|
||||
#endif
|
||||
|
||||
typedef enum { _timer1, _timer2, _timer3, _timer4, _timer5, _Nbr_16timers } timer16_Sequence_t ;
|
||||
|
||||
|
|
@ -1,391 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2015 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if defined(ARDUINO_ARCH_SAMD)
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Servo.h>
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
// Different prescalers depending on FCPU (avoid overflowing 16-bit counter)
|
||||
#if(F_CPU > 200000000)
|
||||
#define usToTicks(_us) ((clockCyclesPerMicrosecond() * _us) / 128)
|
||||
#define ticksToUs(_ticks) (((unsigned) _ticks * 128) / clockCyclesPerMicrosecond())
|
||||
#else
|
||||
#define usToTicks(_us) ((clockCyclesPerMicrosecond() * _us) / 64)
|
||||
#define ticksToUs(_ticks) (((unsigned) _ticks * 64) / clockCyclesPerMicrosecond())
|
||||
#endif
|
||||
#else
|
||||
#define usToTicks(_us) ((clockCyclesPerMicrosecond() * _us) / 16) // converts microseconds to tick
|
||||
#define ticksToUs(_ticks) (((unsigned) _ticks * 16) / clockCyclesPerMicrosecond()) // converts from ticks back to microseconds
|
||||
#endif
|
||||
|
||||
#define TRIM_DURATION 5 // compensation ticks to trim adjust for digitalWrite delays
|
||||
|
||||
static servo_t servos[MAX_SERVOS]; // static array of servo structures
|
||||
|
||||
uint8_t ServoCount = 0; // the total number of attached servos
|
||||
|
||||
static volatile int8_t currentServoIndex[_Nbr_16timers]; // index for the servo being pulsed for each timer (or -1 if refresh interval)
|
||||
|
||||
// convenience macros
|
||||
#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo
|
||||
#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer
|
||||
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
|
||||
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel
|
||||
|
||||
#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
|
||||
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo
|
||||
|
||||
// Referenced in SAMD21 code only, no harm in defining regardless
|
||||
#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY);
|
||||
|
||||
/************ static functions common to all instances ***********************/
|
||||
|
||||
void Servo_Handler(timer16_Sequence_t timer, Tc *pTc, uint8_t channel, uint8_t intFlag);
|
||||
#if defined (_useTimer1)
|
||||
void HANDLER_FOR_TIMER1(void) {
|
||||
Servo_Handler(_timer1, TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, INTFLAG_BIT_FOR_TIMER_1);
|
||||
}
|
||||
#endif
|
||||
#if defined (_useTimer2)
|
||||
void HANDLER_FOR_TIMER2(void) {
|
||||
Servo_Handler(_timer2, TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, INTFLAG_BIT_FOR_TIMER_2);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Servo_Handler(timer16_Sequence_t timer, Tc *tc, uint8_t channel, uint8_t intFlag)
|
||||
{
|
||||
if (currentServoIndex[timer] < 0) {
|
||||
tc->COUNT16.COUNT.reg = (uint16_t) 0;
|
||||
#if defined(__SAMD51__)
|
||||
while(tc->COUNT16.SYNCBUSY.bit.COUNT);
|
||||
#else
|
||||
WAIT_TC16_REGS_SYNC(tc)
|
||||
#endif
|
||||
} else {
|
||||
if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && SERVO(timer, currentServoIndex[timer]).Pin.isActive == true) {
|
||||
digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, LOW); // pulse this channel low if activated
|
||||
}
|
||||
}
|
||||
|
||||
// Select the next servo controlled by this timer
|
||||
currentServoIndex[timer]++;
|
||||
|
||||
if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && currentServoIndex[timer] < SERVOS_PER_TIMER) {
|
||||
if (SERVO(timer, currentServoIndex[timer]).Pin.isActive == true) { // check if activated
|
||||
digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, HIGH); // it's an active channel so pulse it high
|
||||
}
|
||||
|
||||
// Get the counter value
|
||||
#if defined(__SAMD51__)
|
||||
// Note from datasheet: Prior to any read access, this register must be synchronized by user by writing the according TC
|
||||
// Command value to the Control B Set register (CTRLBSET.CMD=READSYNC)
|
||||
while (tc->COUNT16.SYNCBUSY.bit.CTRLB);
|
||||
tc->COUNT16.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_READSYNC_Val;
|
||||
while (tc->COUNT16.SYNCBUSY.bit.CTRLB);
|
||||
#endif
|
||||
uint16_t tcCounterValue = tc->COUNT16.COUNT.reg;
|
||||
#if defined(__SAMD51__)
|
||||
while(tc->COUNT16.SYNCBUSY.bit.COUNT);
|
||||
#else
|
||||
WAIT_TC16_REGS_SYNC(tc)
|
||||
#endif
|
||||
|
||||
tc->COUNT16.CC[channel].reg = (uint16_t) (tcCounterValue + SERVO(timer, currentServoIndex[timer]).ticks);
|
||||
#if defined(__SAMD51__)
|
||||
if(channel == 0) {
|
||||
while(tc->COUNT16.SYNCBUSY.bit.CC0);
|
||||
} else if(channel == 1) {
|
||||
while(tc->COUNT16.SYNCBUSY.bit.CC1);
|
||||
}
|
||||
#else
|
||||
WAIT_TC16_REGS_SYNC(tc)
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
// finished all channels so wait for the refresh period to expire before starting over
|
||||
|
||||
// Get the counter value
|
||||
uint16_t tcCounterValue = tc->COUNT16.COUNT.reg;
|
||||
#if defined(__SAMD51__)
|
||||
while(tc->COUNT16.SYNCBUSY.bit.COUNT);
|
||||
#else
|
||||
WAIT_TC16_REGS_SYNC(tc)
|
||||
#endif
|
||||
|
||||
if (tcCounterValue + 4UL < usToTicks(REFRESH_INTERVAL)) { // allow a few ticks to ensure the next OCR1A not missed
|
||||
tc->COUNT16.CC[channel].reg = (uint16_t) usToTicks(REFRESH_INTERVAL);
|
||||
}
|
||||
else {
|
||||
tc->COUNT16.CC[channel].reg = (uint16_t) (tcCounterValue + 4UL); // at least REFRESH_INTERVAL has elapsed
|
||||
}
|
||||
#if defined(__SAMD51__)
|
||||
if(channel == 0) {
|
||||
while(tc->COUNT16.SYNCBUSY.bit.CC0);
|
||||
} else if(channel == 1) {
|
||||
while(tc->COUNT16.SYNCBUSY.bit.CC1);
|
||||
}
|
||||
#else
|
||||
WAIT_TC16_REGS_SYNC(tc)
|
||||
#endif
|
||||
|
||||
currentServoIndex[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
|
||||
}
|
||||
|
||||
// Clear the interrupt
|
||||
tc->COUNT16.INTFLAG.reg = intFlag;
|
||||
}
|
||||
|
||||
static inline void resetTC (Tc* TCx)
|
||||
{
|
||||
// Disable TCx
|
||||
TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
|
||||
#if defined(__SAMD51__)
|
||||
while(TCx->COUNT16.SYNCBUSY.bit.ENABLE);
|
||||
#else
|
||||
WAIT_TC16_REGS_SYNC(TCx)
|
||||
#endif
|
||||
|
||||
// Reset TCx
|
||||
TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
|
||||
#if defined(__SAMD51__)
|
||||
while(TCx->COUNT16.SYNCBUSY.bit.SWRST);
|
||||
#else
|
||||
WAIT_TC16_REGS_SYNC(TCx)
|
||||
#endif
|
||||
while (TCx->COUNT16.CTRLA.bit.SWRST);
|
||||
}
|
||||
|
||||
static void _initISR(Tc *tc, uint8_t channel, uint32_t id, IRQn_Type irqn, uint8_t gcmForTimer, uint8_t intEnableBit)
|
||||
{
|
||||
(void)id;
|
||||
// Select GCLK0 as timer/counter input clock source
|
||||
#if defined(__SAMD51__)
|
||||
int idx = gcmForTimer; // see datasheet Table 14-9
|
||||
GCLK->PCHCTRL[idx].bit.GEN = 0; // Select GCLK0 as periph clock source
|
||||
GCLK->PCHCTRL[idx].bit.CHEN = 1; // Enable peripheral
|
||||
while(!GCLK->PCHCTRL[idx].bit.CHEN);
|
||||
#else
|
||||
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(gcmForTimer));
|
||||
while (GCLK->STATUS.bit.SYNCBUSY);
|
||||
#endif
|
||||
|
||||
// Reset the timer
|
||||
// TODO this is not the right thing to do if more than one channel per timer is used by the Servo library
|
||||
resetTC(tc);
|
||||
|
||||
// Set timer counter mode to 16 bits
|
||||
tc->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
// Set timer counter mode as normal PWM
|
||||
tc->COUNT16.WAVE.bit.WAVEGEN = TCC_WAVE_WAVEGEN_NPWM_Val;
|
||||
|
||||
// Set the prescaler factor to 64 or 128 depending on FCPU
|
||||
// (avoid overflowing 16-bit clock counter)
|
||||
#if(F_CPU > 200000000)
|
||||
tc->COUNT16.CTRLA.bit.PRESCALER = TCC_CTRLA_PRESCALER_DIV128_Val;
|
||||
#else
|
||||
// At 120-200 MHz GCLK this is 1875-3125 ticks per millisecond
|
||||
tc->COUNT16.CTRLA.bit.PRESCALER = TCC_CTRLA_PRESCALER_DIV64_Val;
|
||||
#endif
|
||||
#else
|
||||
// Set timer counter mode as normal PWM
|
||||
tc->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_NPWM;
|
||||
|
||||
// Set the prescaler factor to GCLK_TC/16. At nominal 48MHz GCLK_TC this is 3000 ticks per millisecond
|
||||
tc->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV16;
|
||||
#endif
|
||||
|
||||
// Count up
|
||||
tc->COUNT16.CTRLBCLR.bit.DIR = 1;
|
||||
#if defined(__SAMD51__)
|
||||
while(tc->COUNT16.SYNCBUSY.bit.CTRLB);
|
||||
#else
|
||||
WAIT_TC16_REGS_SYNC(tc)
|
||||
#endif
|
||||
|
||||
// First interrupt request after 1 ms
|
||||
tc->COUNT16.CC[channel].reg = (uint16_t) usToTicks(1000UL);
|
||||
#if defined(__SAMD51__)
|
||||
if(channel == 0) {
|
||||
while(tc->COUNT16.SYNCBUSY.bit.CC0);
|
||||
} else if(channel == 1) {
|
||||
while(tc->COUNT16.SYNCBUSY.bit.CC1);
|
||||
}
|
||||
#else
|
||||
WAIT_TC16_REGS_SYNC(tc)
|
||||
#endif
|
||||
|
||||
// Configure interrupt request
|
||||
// TODO this should be changed if more than one channel per timer is used by the Servo library
|
||||
NVIC_DisableIRQ(irqn);
|
||||
NVIC_ClearPendingIRQ(irqn);
|
||||
NVIC_SetPriority(irqn, 0);
|
||||
NVIC_EnableIRQ(irqn);
|
||||
|
||||
// Enable the match channel interrupt request
|
||||
tc->COUNT16.INTENSET.reg = intEnableBit;
|
||||
|
||||
// Enable the timer and start it
|
||||
tc->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
|
||||
#if defined(__SAMD51__)
|
||||
while(tc->COUNT16.SYNCBUSY.bit.ENABLE);
|
||||
#else
|
||||
WAIT_TC16_REGS_SYNC(tc)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void initISR(timer16_Sequence_t timer)
|
||||
{
|
||||
#if defined (_useTimer1)
|
||||
if (timer == _timer1)
|
||||
_initISR(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, ID_TC_FOR_TIMER1, IRQn_FOR_TIMER1, GCM_FOR_TIMER_1, INTENSET_BIT_FOR_TIMER_1);
|
||||
#endif
|
||||
#if defined (_useTimer2)
|
||||
if (timer == _timer2)
|
||||
_initISR(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, ID_TC_FOR_TIMER2, IRQn_FOR_TIMER2, GCM_FOR_TIMER_2, INTENSET_BIT_FOR_TIMER_2);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void finISR(timer16_Sequence_t timer)
|
||||
{
|
||||
(void)timer;
|
||||
#if defined (_useTimer1)
|
||||
// Disable the match channel interrupt request
|
||||
TC_FOR_TIMER1->COUNT16.INTENCLR.reg = INTENCLR_BIT_FOR_TIMER_1;
|
||||
#endif
|
||||
#if defined (_useTimer2)
|
||||
// Disable the match channel interrupt request
|
||||
TC_FOR_TIMER2->COUNT16.INTENCLR.reg = INTENCLR_BIT_FOR_TIMER_2;
|
||||
#endif
|
||||
}
|
||||
|
||||
static boolean isTimerActive(timer16_Sequence_t timer)
|
||||
{
|
||||
// returns true if any servo is active on this timer
|
||||
for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) {
|
||||
if(SERVO(timer,channel).Pin.isActive == true)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/****************** end of static functions ******************************/
|
||||
|
||||
Servo::Servo()
|
||||
{
|
||||
if (ServoCount < MAX_SERVOS) {
|
||||
this->servoIndex = ServoCount++; // assign a servo index to this instance
|
||||
servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values
|
||||
} else {
|
||||
this->servoIndex = INVALID_SERVO; // too many servos
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Servo::attach(int pin)
|
||||
{
|
||||
return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
|
||||
}
|
||||
|
||||
uint8_t Servo::attach(int pin, int min, int max)
|
||||
{
|
||||
timer16_Sequence_t timer;
|
||||
|
||||
if (this->servoIndex < MAX_SERVOS) {
|
||||
pinMode(pin, OUTPUT); // set servo pin to output
|
||||
servos[this->servoIndex].Pin.nbr = pin;
|
||||
// todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
|
||||
this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
|
||||
this->max = (MAX_PULSE_WIDTH - max)/4;
|
||||
// initialize the timer if it has not already been initialized
|
||||
timer = SERVO_INDEX_TO_TIMER(servoIndex);
|
||||
if (isTimerActive(timer) == false) {
|
||||
initISR(timer);
|
||||
}
|
||||
servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive
|
||||
}
|
||||
return this->servoIndex;
|
||||
}
|
||||
|
||||
void Servo::detach()
|
||||
{
|
||||
timer16_Sequence_t timer;
|
||||
|
||||
servos[this->servoIndex].Pin.isActive = false;
|
||||
timer = SERVO_INDEX_TO_TIMER(servoIndex);
|
||||
if(isTimerActive(timer) == false) {
|
||||
finISR(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void Servo::write(int value)
|
||||
{
|
||||
// treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
|
||||
if (value < MIN_PULSE_WIDTH)
|
||||
{
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
else if (value > 180)
|
||||
value = 180;
|
||||
|
||||
value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
|
||||
}
|
||||
writeMicroseconds(value);
|
||||
}
|
||||
|
||||
void Servo::writeMicroseconds(int value)
|
||||
{
|
||||
// calculate and store the values for the given channel
|
||||
byte channel = this->servoIndex;
|
||||
if( (channel < MAX_SERVOS) ) // ensure channel is valid
|
||||
{
|
||||
if (value < SERVO_MIN()) // ensure pulse width is valid
|
||||
value = SERVO_MIN();
|
||||
else if (value > SERVO_MAX())
|
||||
value = SERVO_MAX();
|
||||
|
||||
value = value - TRIM_DURATION;
|
||||
value = usToTicks(value); // convert to ticks after compensating for interrupt overhead
|
||||
servos[channel].ticks = value;
|
||||
}
|
||||
}
|
||||
|
||||
int Servo::read() // return the value as degrees
|
||||
{
|
||||
return map(readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
|
||||
}
|
||||
|
||||
int Servo::readMicroseconds()
|
||||
{
|
||||
unsigned int pulsewidth;
|
||||
if (this->servoIndex != INVALID_SERVO)
|
||||
pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION;
|
||||
else
|
||||
pulsewidth = 0;
|
||||
|
||||
return pulsewidth;
|
||||
}
|
||||
|
||||
bool Servo::attached()
|
||||
{
|
||||
return servos[this->servoIndex].Pin.isActive;
|
||||
}
|
||||
|
||||
#endif // ARDUINO_ARCH_SAMD
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2015 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Defines for 16 bit timers used with Servo library
|
||||
*
|
||||
* If _useTimerX is defined then TimerX is a 16 bit timer on the current board
|
||||
* timer16_Sequence_t enumerates the sequence that the timers should be allocated
|
||||
* _Nbr_16timers indicates how many 16 bit timers are available.
|
||||
*/
|
||||
|
||||
#ifndef __SERVO_TIMERS_H__
|
||||
#define __SERVO_TIMERS_H__
|
||||
|
||||
/**
|
||||
* SAMD Only definitions
|
||||
* ---------------------
|
||||
*/
|
||||
|
||||
// For SAMD:
|
||||
#define _useTimer1
|
||||
//#define _useTimer2 // <- TODO do not activate until the code in Servo.cpp has been changed in order
|
||||
// to manage more than one channel per timer on the SAMD architecture
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
#if defined (_useTimer1)
|
||||
#define TC_FOR_TIMER1 TC1
|
||||
#define CHANNEL_FOR_TIMER1 0
|
||||
#define INTENSET_BIT_FOR_TIMER_1 TC_INTENSET_MC0
|
||||
#define INTENCLR_BIT_FOR_TIMER_1 TC_INTENCLR_MC0
|
||||
#define INTFLAG_BIT_FOR_TIMER_1 TC_INTFLAG_MC0
|
||||
#define ID_TC_FOR_TIMER1 ID_TC1
|
||||
#define IRQn_FOR_TIMER1 TC1_IRQn
|
||||
#define HANDLER_FOR_TIMER1 TC1_Handler
|
||||
#define GCM_FOR_TIMER_1 TC1_GCLK_ID
|
||||
#endif
|
||||
|
||||
#if defined (_useTimer2)
|
||||
#define TC_FOR_TIMER2 TC1
|
||||
#define CHANNEL_FOR_TIMER2 1
|
||||
#define INTENSET_BIT_FOR_TIMER_2 TC_INTENSET_MC1
|
||||
#define INTENCLR_BIT_FOR_TIMER_2 TC_INTENCLR_MC1
|
||||
#define INTFLAG_BIT_FOR_TIMER_2 TC_INTFLAG_MC1
|
||||
#define ID_TC_FOR_TIMER2 ID_TC1
|
||||
#define IRQn_FOR_TIMER2 TC1_IRQn
|
||||
#define HANDLER_FOR_TIMER2 TC1_Handler
|
||||
#define GCM_FOR_TIMER_2 TC1_GCLK_ID
|
||||
#endif
|
||||
#else
|
||||
#if defined (_useTimer1)
|
||||
#define TC_FOR_TIMER1 TC4
|
||||
#define CHANNEL_FOR_TIMER1 0
|
||||
#define INTENSET_BIT_FOR_TIMER_1 TC_INTENSET_MC0
|
||||
#define INTENCLR_BIT_FOR_TIMER_1 TC_INTENCLR_MC0
|
||||
#define INTFLAG_BIT_FOR_TIMER_1 TC_INTFLAG_MC0
|
||||
#define ID_TC_FOR_TIMER1 ID_TC4
|
||||
#define IRQn_FOR_TIMER1 TC4_IRQn
|
||||
#define HANDLER_FOR_TIMER1 TC4_Handler
|
||||
#define GCM_FOR_TIMER_1 GCM_TC4_TC5
|
||||
#endif
|
||||
|
||||
#if defined (_useTimer2)
|
||||
#define TC_FOR_TIMER2 TC4
|
||||
#define CHANNEL_FOR_TIMER2 1
|
||||
#define INTENSET_BIT_FOR_TIMER_2 TC_INTENSET_MC1
|
||||
#define INTENCLR_BIT_FOR_TIMER_2 TC_INTENCLR_MC1
|
||||
#define ID_TC_FOR_TIMER2 ID_TC4
|
||||
#define IRQn_FOR_TIMER2 TC4_IRQn
|
||||
#define HANDLER_FOR_TIMER2 TC4_Handler
|
||||
#define GCM_FOR_TIMER_2 GCM_TC4_TC5
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
#if defined (_useTimer1)
|
||||
_timer1,
|
||||
#endif
|
||||
#if defined (_useTimer2)
|
||||
_timer2,
|
||||
#endif
|
||||
_Nbr_16timers } timer16_Sequence_t;
|
||||
|
||||
#endif // __SERVO_TIMERS_H__
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010, LeafLabs, LLC.
|
||||
*
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#if defined(ARDUINO_ARCH_STM32F4)
|
||||
|
||||
#include "ServoTimers.h"
|
||||
|
||||
#include "boards.h"
|
||||
#include "io.h"
|
||||
#include "pwm.h"
|
||||
#include "math.h"
|
||||
|
||||
// 20 millisecond period config. For a 1-based prescaler,
|
||||
//
|
||||
// (prescaler * overflow / CYC_MSEC) msec = 1 timer cycle = 20 msec
|
||||
// => prescaler * overflow = 20 * CYC_MSEC
|
||||
//
|
||||
// This picks the smallest prescaler that allows an overflow < 2^16.
|
||||
#define MAX_OVERFLOW ((1 << 16) - 1)
|
||||
#define CYC_MSEC (1000 * CYCLES_PER_MICROSECOND)
|
||||
#define TAU_MSEC 20
|
||||
#define TAU_USEC (TAU_MSEC * 1000)
|
||||
#define TAU_CYC (TAU_MSEC * CYC_MSEC)
|
||||
#define SERVO_PRESCALER (TAU_CYC / MAX_OVERFLOW + 1)
|
||||
#define SERVO_OVERFLOW ((uint16)round((double)TAU_CYC / SERVO_PRESCALER))
|
||||
|
||||
// Unit conversions
|
||||
#define US_TO_COMPARE(us) ((uint16)map((us), 0, TAU_USEC, 0, SERVO_OVERFLOW))
|
||||
#define COMPARE_TO_US(c) ((uint32)map((c), 0, SERVO_OVERFLOW, 0, TAU_USEC))
|
||||
#define ANGLE_TO_US(a) ((uint16)(map((a), this->minAngle, this->maxAngle, \
|
||||
this->minPW, this->maxPW)))
|
||||
#define US_TO_ANGLE(us) ((int16)(map((us), this->minPW, this->maxPW, \
|
||||
this->minAngle, this->maxAngle)))
|
||||
|
||||
Servo::Servo() {
|
||||
this->resetFields();
|
||||
}
|
||||
|
||||
bool Servo::attach(uint8 pin, uint16 minPW, uint16 maxPW, int16 minAngle, int16 maxAngle)
|
||||
{
|
||||
// SerialUSB.begin(115200);
|
||||
// SerialUSB.println(MAX_OVERFLOW);
|
||||
|
||||
|
||||
timer_dev *tdev = PIN_MAP[pin].timer_device;
|
||||
|
||||
analogWriteResolution(16);
|
||||
|
||||
int prescaler = 6;
|
||||
int overflow = 65400;
|
||||
int minPW_correction = 300;
|
||||
int maxPW_correction = 300;
|
||||
|
||||
pinMode(pin, OUTPUT);
|
||||
|
||||
|
||||
if (tdev == NULL) {
|
||||
// don't reset any fields or ASSERT(0), to keep driving any
|
||||
// previously attach()ed servo.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( (tdev == TIMER1) || (tdev == TIMER8) || (tdev == TIMER10) || (tdev == TIMER11))
|
||||
{
|
||||
prescaler = 54;
|
||||
overflow = 65400;
|
||||
minPW_correction = 40;
|
||||
maxPW_correction = 50;
|
||||
}
|
||||
|
||||
if ( (tdev == TIMER2) || (tdev == TIMER3) || (tdev == TIMER4) || (tdev == TIMER5) )
|
||||
{
|
||||
prescaler = 6;
|
||||
overflow = 64285;
|
||||
minPW_correction = 370;
|
||||
maxPW_correction = 350;
|
||||
}
|
||||
|
||||
if ( (tdev == TIMER6) || (tdev == TIMER7) )
|
||||
{
|
||||
prescaler = 6;
|
||||
overflow = 65400;
|
||||
minPW_correction = 0;
|
||||
maxPW_correction = 0;
|
||||
}
|
||||
|
||||
if ( (tdev == TIMER9) || (tdev == TIMER12) || (tdev == TIMER13) || (tdev == TIMER14) )
|
||||
{
|
||||
prescaler = 6;
|
||||
overflow = 65400;
|
||||
minPW_correction = 30;
|
||||
maxPW_correction = 0;
|
||||
}
|
||||
|
||||
if (this->attached()) {
|
||||
this->detach();
|
||||
}
|
||||
|
||||
this->pin = pin;
|
||||
this->minPW = (minPW + minPW_correction);
|
||||
this->maxPW = (maxPW + maxPW_correction);
|
||||
this->minAngle = minAngle;
|
||||
this->maxAngle = maxAngle;
|
||||
|
||||
timer_pause(tdev);
|
||||
timer_set_prescaler(tdev, prescaler); // prescaler is 1-based
|
||||
timer_set_reload(tdev, overflow);
|
||||
timer_generate_update(tdev);
|
||||
timer_resume(tdev);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Servo::detach() {
|
||||
if (!this->attached()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
timer_dev *tdev = PIN_MAP[this->pin].timer_device;
|
||||
uint8 tchan = PIN_MAP[this->pin].timer_channel;
|
||||
timer_set_mode(tdev, tchan, TIMER_DISABLED);
|
||||
|
||||
this->resetFields();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Servo::write(int degrees) {
|
||||
degrees = constrain(degrees, this->minAngle, this->maxAngle);
|
||||
this->writeMicroseconds(ANGLE_TO_US(degrees));
|
||||
}
|
||||
|
||||
int Servo::read() const {
|
||||
int a = US_TO_ANGLE(this->readMicroseconds());
|
||||
// map() round-trips in a weird way we mostly correct for here;
|
||||
// the round-trip is still sometimes off-by-one for write(1) and
|
||||
// write(179).
|
||||
return a == this->minAngle || a == this->maxAngle ? a : a + 1;
|
||||
}
|
||||
|
||||
void Servo::writeMicroseconds(uint16 pulseWidth) {
|
||||
if (!this->attached()) {
|
||||
ASSERT(0);
|
||||
return;
|
||||
}
|
||||
pulseWidth = constrain(pulseWidth, this->minPW, this->maxPW);
|
||||
analogWrite(this->pin, US_TO_COMPARE(pulseWidth));
|
||||
}
|
||||
|
||||
uint16 Servo::readMicroseconds() const {
|
||||
if (!this->attached()) {
|
||||
ASSERT(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
stm32_pin_info pin_info = PIN_MAP[this->pin];
|
||||
uint16 compare = timer_get_compare(pin_info.timer_device,
|
||||
pin_info.timer_channel);
|
||||
|
||||
return COMPARE_TO_US(compare);
|
||||
}
|
||||
|
||||
void Servo::resetFields(void) {
|
||||
this->pin = NOT_ATTACHED;
|
||||
this->minAngle = MIN_ANGLE;
|
||||
this->maxAngle = MAX_ANGLE;
|
||||
this->minPW = MIN_PULSE_WIDTH;
|
||||
this->maxPW = MAX_PULSE_WIDTH;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010, LeafLabs, LLC.
|
||||
*
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* Arduino srl - www.arduino.org
|
||||
* 2017 Feb 23: Edited by Francesco Alessi (alfran) - francesco@arduino.org
|
||||
*/
|
||||
#ifndef _SERVO_H_
|
||||
#define _SERVO_H_
|
||||
|
||||
#include "types.h"
|
||||
#include "timer.h"
|
||||
|
||||
#include "wiring.h" /* hack for IDE compile */
|
||||
|
||||
/*
|
||||
* Note on Arduino compatibility:
|
||||
*
|
||||
* In the Arduino implementation, PWM is done "by hand" in the sense
|
||||
* that timer channels are hijacked in groups and an ISR is set which
|
||||
* toggles Servo::attach()ed pins using digitalWrite().
|
||||
*
|
||||
* While this scheme allows any pin to drive a servo, it chews up
|
||||
* cycles and complicates the programmer's notion of when a particular
|
||||
* timer channel will be in use.
|
||||
*
|
||||
* This implementation only allows Servo instances to attach() to pins
|
||||
* that already have a timer channel associated with them, and just
|
||||
* uses pwmWrite() to drive the wave.
|
||||
*
|
||||
* This introduces an incompatibility: while the Arduino
|
||||
* implementation of attach() returns the affected channel on success
|
||||
* and 0 on failure, this one returns true on success and false on
|
||||
* failure.
|
||||
*
|
||||
* RC Servos expect a pulse every 20ms. Since periods are set for
|
||||
* entire timers, rather than individual channels, attach()ing a Servo
|
||||
* to a pin can interfere with other pins associated with the same
|
||||
* timer. As always, your board's pin map is your friend.
|
||||
*/
|
||||
|
||||
// Pin number of unattached pins
|
||||
#define NOT_ATTACHED (-1)
|
||||
|
||||
#define _Nbr_16timers 14 // mumber of STM32F469 Timers
|
||||
#define SERVOS_PER_TIMER 4 // Number of timer channels
|
||||
|
||||
|
||||
// Default min/max pulse widths (in microseconds) and angles (in
|
||||
// degrees). Values chosen for Arduino compatibility. These values
|
||||
// are part of the public API; DO NOT CHANGE THEM.
|
||||
#define MIN_ANGLE 0
|
||||
#define MAX_ANGLE 180
|
||||
|
||||
#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
|
||||
#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
|
||||
|
||||
/** Class for interfacing with RC servomotors. */
|
||||
class Servo {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Servo instance.
|
||||
*
|
||||
* The new instance will not be attached to any pin.
|
||||
*/
|
||||
Servo();
|
||||
|
||||
/**
|
||||
* @brief Associate this instance with a servomotor whose input is
|
||||
* connected to pin.
|
||||
*
|
||||
* If this instance is already attached to a pin, it will be
|
||||
* detached before being attached to the new pin. This function
|
||||
* doesn't detach any interrupt attached with the pin's timer
|
||||
* channel.
|
||||
*
|
||||
* @param pin Pin connected to the servo pulse wave input. This
|
||||
* pin must be capable of PWM output.
|
||||
*
|
||||
* @param minPulseWidth Minimum pulse width to write to pin, in
|
||||
* microseconds. This will be associated
|
||||
* with a minAngle degree angle. Defaults to
|
||||
* SERVO_DEFAULT_MIN_PW = 544.
|
||||
*
|
||||
* @param maxPulseWidth Maximum pulse width to write to pin, in
|
||||
* microseconds. This will be associated
|
||||
* with a maxAngle degree angle. Defaults to
|
||||
* SERVO_DEFAULT_MAX_PW = 2400.
|
||||
*
|
||||
* @param minAngle Target angle (in degrees) associated with
|
||||
* minPulseWidth. Defaults to
|
||||
* SERVO_DEFAULT_MIN_ANGLE = 0.
|
||||
*
|
||||
* @param maxAngle Target angle (in degrees) associated with
|
||||
* maxPulseWidth. Defaults to
|
||||
* SERVO_DEFAULT_MAX_ANGLE = 180.
|
||||
*
|
||||
* @sideeffect May set pinMode(pin, PWM).
|
||||
*
|
||||
* @return true if successful, false when pin doesn't support PWM.
|
||||
*/
|
||||
|
||||
bool attach(uint8 pin,
|
||||
uint16 minPulseWidth=MIN_PULSE_WIDTH,
|
||||
uint16 maxPulseWidth=MAX_PULSE_WIDTH,
|
||||
int16 minAngle=MIN_ANGLE,
|
||||
int16 maxAngle=MAX_ANGLE);
|
||||
/**
|
||||
* @brief Stop driving the servo pulse train.
|
||||
*
|
||||
* If not currently attached to a motor, this function has no effect.
|
||||
*
|
||||
* @return true if this call did anything, false otherwise.
|
||||
*/
|
||||
bool detach();
|
||||
|
||||
/**
|
||||
* @brief Set the servomotor target angle.
|
||||
*
|
||||
* @param angle Target angle, in degrees. If the target angle is
|
||||
* outside the range specified at attach() time, it
|
||||
* will be clamped to lie in that range.
|
||||
*
|
||||
* @see Servo::attach()
|
||||
*/
|
||||
void write(int angle);
|
||||
|
||||
/**
|
||||
* @brief Set the pulse width, in microseconds.
|
||||
*
|
||||
* @param pulseWidth Pulse width to send to the servomotor, in
|
||||
* microseconds. If outside of the range
|
||||
* specified at attach() time, it is clamped to
|
||||
* lie in that range.
|
||||
*
|
||||
* @see Servo::attach()
|
||||
*/
|
||||
void writeMicroseconds(uint16 pulseWidth);
|
||||
|
||||
/**
|
||||
* Get the servomotor's target angle, in degrees. This will
|
||||
* lie inside the range specified at attach() time.
|
||||
*
|
||||
* @see Servo::attach()
|
||||
*/
|
||||
int read() const;
|
||||
|
||||
/**
|
||||
* Get the current pulse width, in microseconds. This will
|
||||
* lie within the range specified at attach() time.
|
||||
*
|
||||
* @see Servo::attach()
|
||||
*/
|
||||
uint16 readMicroseconds() const;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Check if this instance is attached to a servo.
|
||||
* @return true if this instance is attached to a servo, false otherwise.
|
||||
* @see Servo::attachedPin()
|
||||
*/
|
||||
bool attached() const { return this->pin != NOT_ATTACHED; }
|
||||
|
||||
/**
|
||||
* @brief Get the pin this instance is attached to.
|
||||
* @return Pin number if currently attached to a pin, NOT_ATTACHED
|
||||
* otherwise.
|
||||
* @see Servo::attach()
|
||||
*/
|
||||
int attachedPin() const { return this->pin; }
|
||||
|
||||
private:
|
||||
int16 pin;
|
||||
uint16 minPW;
|
||||
uint16 maxPW;
|
||||
int16 minAngle;
|
||||
int16 maxAngle;
|
||||
|
||||
void resetFields(void);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* _SERVO_H_ */
|
||||
|
|
@ -37,7 +37,7 @@ void setup(void)
|
|||
while (!SERIAL_PORT_MONITOR); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
|
||||
SERIAL_PORT_MONITOR.println("\r\nADK demo start");
|
||||
|
||||
if (usb.Init() == (uint32_t)-1)
|
||||
if (usb.Init() == -1)
|
||||
SERIAL_PORT_MONITOR.println("OSC did not start.");
|
||||
|
||||
delay(20);
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ void setup()
|
|||
{
|
||||
SerialDebug.begin( 115200 );
|
||||
SerialDebug.println("USB Host Keyboard Controller Program started");
|
||||
if (usb.Init() == (uint32_t)-1)
|
||||
|
||||
if (usb.Init() == -1)
|
||||
SerialDebug.println("USB Host did not start.");
|
||||
|
||||
SerialDebug.println("USB Host started");
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ void setup()
|
|||
SerialDebug.begin( 115200 );
|
||||
SerialDebug.println("USB Host Mouse Controller Program started");
|
||||
|
||||
if (usb.Init() == (uint32_t)-1)
|
||||
if (usb.Init() == -1)
|
||||
SerialDebug.println("USB Host did not start.");
|
||||
|
||||
SerialDebug.println("USB Host started");
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ void setup()
|
|||
SerialDebug.println("Starting USB Descriptor test");
|
||||
|
||||
SerialDebug.println("Initializing USB");
|
||||
if (usb.Init() == (uint32_t)-1)
|
||||
SerialDebug.println("USBhost did not start.");
|
||||
if (usb.Init() == -1)
|
||||
SerialDebug.println("USBhost did not start.");
|
||||
|
||||
delay( 20 );
|
||||
|
||||
|
|
@ -161,7 +161,6 @@ byte getdevdescr( byte addr, byte &num_conf )
|
|||
|
||||
void printhubdescr(uint8_t *descrptr, uint8_t addr)
|
||||
{
|
||||
(void)addr;
|
||||
HubDescriptor *pHub = (HubDescriptor*) descrptr;
|
||||
uint8_t len = *((uint8_t*)descrptr);
|
||||
|
||||
|
|
@ -210,11 +209,10 @@ byte getconfdescr( byte addr, byte conf )
|
|||
{
|
||||
uint8_t buf[ BUFSIZE ];
|
||||
uint8_t* buf_ptr = buf;
|
||||
byte rcode; // FIXME -- code does not actually check return code (no error handling!)
|
||||
byte rcode;
|
||||
byte descr_length;
|
||||
byte descr_type;
|
||||
uint16_t total_length;
|
||||
// FIXME -- no check of return code from usb.getConfDescr()
|
||||
rcode = usb.getConfDescr( addr, 0, 4, conf, buf ); //get total length
|
||||
LOBYTE( total_length ) = buf[ 2 ];
|
||||
HIBYTE( total_length ) = buf[ 3 ];
|
||||
|
|
@ -222,7 +220,6 @@ byte getconfdescr( byte addr, byte conf )
|
|||
printProgStr(Conf_Trunc_str);
|
||||
total_length = sizeof(buf);
|
||||
}
|
||||
// FIXME -- no check of return code from usb.getConfDescr()
|
||||
rcode = usb.getConfDescr( addr, 0, total_length, conf, buf ); //get the whole descriptor
|
||||
while( buf_ptr < buf + total_length ) { //parsing descriptors
|
||||
descr_length = *( buf_ptr );
|
||||
|
|
@ -385,4 +382,3 @@ void printProgStr(const prog_char str[])
|
|||
if(!str) return;
|
||||
while((c = pgm_read_byte(str++)))
|
||||
SerialDebug.print(c);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,15 +59,15 @@ class ConfigDescParser : public USBReadParser {
|
|||
uint32_t ifaceNumber; // Interface number
|
||||
uint32_t ifaceAltSet; // Interface alternate settings
|
||||
|
||||
bool UseOr;
|
||||
bool UseOr;
|
||||
bool ParseDescriptor(uint8_t **pp, uint32_t *pcntdn);
|
||||
void PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc);
|
||||
|
||||
public:
|
||||
|
||||
void SetOR(void) {
|
||||
UseOr = true;
|
||||
}
|
||||
void SetOR(void) {
|
||||
UseOr = true;
|
||||
}
|
||||
ConfigDescParser(UsbConfigXtracter *xtractor);
|
||||
virtual void Parse(const uint32_t len, const uint8_t *pbuf, const uint32_t &offset);
|
||||
};
|
||||
|
|
@ -98,19 +98,8 @@ void ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::Parse(const uin
|
|||
compare masks for them. When the match is found, calls EndpointXtract passing buffer containing endpoint descriptor */
|
||||
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
|
||||
bool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor(uint8_t **pp, uint32_t *pcntdn) {
|
||||
USB_CONFIGURATION_DESCRIPTOR* ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR*>(varBuffer);
|
||||
USB_INTERFACE_DESCRIPTOR* uid = reinterpret_cast<USB_INTERFACE_DESCRIPTOR*>(varBuffer);
|
||||
|
||||
|
||||
#pragma GCC diagnostic push // Available since GCC 4.6.4
|
||||
/*
|
||||
* FIXME -- Enabled and review all `-Wimplicit-fallthrough` messages
|
||||
* This code has multiple switch statements that "fall through" to the
|
||||
* next case -- but it's not always clear if this is intentional or not.
|
||||
* Review and commenting of code, and reducing cyclomatic complexity
|
||||
* are highly recommended....
|
||||
*/
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
USB_CONFIGURATION_DESCRIPTOR* ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR*>(varBuffer);
|
||||
USB_INTERFACE_DESCRIPTOR* uid = reinterpret_cast<USB_INTERFACE_DESCRIPTOR*>(varBuffer);
|
||||
switch(stateParseDescr) {
|
||||
case 0:
|
||||
theBuffer.valueSize = 2;
|
||||
|
|
@ -123,7 +112,7 @@ bool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor
|
|||
dscrType = *((uint8_t*)theBuffer.pValue + 1);
|
||||
stateParseDescr = 2;
|
||||
case 2:
|
||||
// This is a sort of hack. Assuming that two bytes are all ready in the buffer
|
||||
// This is a sort of hack. Assuming that two bytes are all ready in the buffer
|
||||
// the pointer is positioned two bytes ahead in order for the rest of descriptor
|
||||
// to be read right after the size and the type fields.
|
||||
// This should be used carefully. varBuffer should be used directly to handle data
|
||||
|
|
@ -131,14 +120,14 @@ bool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor
|
|||
theBuffer.pValue = varBuffer + 2;
|
||||
stateParseDescr = 3;
|
||||
case 3:
|
||||
switch(dscrType) {
|
||||
case USB_DESCRIPTOR_INTERFACE:
|
||||
isGoodInterface = false;
|
||||
case USB_DESCRIPTOR_CONFIGURATION:
|
||||
theBuffer.valueSize = sizeof (USB_CONFIGURATION_DESCRIPTOR) - 2;
|
||||
break;
|
||||
case USB_DESCRIPTOR_ENDPOINT:
|
||||
theBuffer.valueSize = sizeof (USB_ENDPOINT_DESCRIPTOR) - 2;
|
||||
switch(dscrType) {
|
||||
case USB_DESCRIPTOR_INTERFACE:
|
||||
isGoodInterface = false;
|
||||
case USB_DESCRIPTOR_CONFIGURATION:
|
||||
theBuffer.valueSize = sizeof (USB_CONFIGURATION_DESCRIPTOR) - 2;
|
||||
break;
|
||||
case USB_DESCRIPTOR_ENDPOINT:
|
||||
theBuffer.valueSize = sizeof (USB_ENDPOINT_DESCRIPTOR) - 2;
|
||||
break;
|
||||
case HID_DESCRIPTOR_HID:
|
||||
theBuffer.valueSize = dscrLen - 2;
|
||||
|
|
@ -147,37 +136,37 @@ bool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor
|
|||
valParser.Initialize(&theBuffer);
|
||||
stateParseDescr = 4;
|
||||
case 4:
|
||||
switch(dscrType) {
|
||||
switch(dscrType) {
|
||||
case USB_DESCRIPTOR_CONFIGURATION:
|
||||
if(!valParser.Parse(pp, pcntdn))
|
||||
return false;
|
||||
confValue = ucd->bConfigurationValue;
|
||||
if(!valParser.Parse(pp, pcntdn))
|
||||
return false;
|
||||
confValue = ucd->bConfigurationValue;
|
||||
break;
|
||||
case USB_DESCRIPTOR_INTERFACE:
|
||||
if(!valParser.Parse(pp, pcntdn))
|
||||
return false;
|
||||
if((MASK & CP_MASK_COMPARE_CLASS) && uid->bInterfaceClass != CLASS_ID)
|
||||
break;
|
||||
if((MASK & CP_MASK_COMPARE_SUBCLASS) && uid->bInterfaceSubClass != SUBCLASS_ID)
|
||||
break;
|
||||
if(UseOr) {
|
||||
if((!((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol)))
|
||||
break;
|
||||
} else {
|
||||
if((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol != PROTOCOL_ID)
|
||||
break;
|
||||
}
|
||||
isGoodInterface = true;
|
||||
ifaceNumber = uid->bInterfaceNumber;
|
||||
ifaceAltSet = uid->bAlternateSetting;
|
||||
protoValue = uid->bInterfaceProtocol;
|
||||
break;
|
||||
case USB_DESCRIPTOR_ENDPOINT:
|
||||
if(!valParser.Parse(pp, pcntdn))
|
||||
return false;
|
||||
if(isGoodInterface)
|
||||
if(theXtractor)
|
||||
theXtractor->EndpointXtract(confValue, ifaceNumber, ifaceAltSet, protoValue, (USB_ENDPOINT_DESCRIPTOR*)varBuffer);
|
||||
if(!valParser.Parse(pp, pcntdn))
|
||||
return false;
|
||||
if((MASK & CP_MASK_COMPARE_CLASS) && uid->bInterfaceClass != CLASS_ID)
|
||||
break;
|
||||
if((MASK & CP_MASK_COMPARE_SUBCLASS) && uid->bInterfaceSubClass != SUBCLASS_ID)
|
||||
break;
|
||||
if(UseOr) {
|
||||
if((!((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol)))
|
||||
break;
|
||||
} else {
|
||||
if((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol != PROTOCOL_ID)
|
||||
break;
|
||||
}
|
||||
isGoodInterface = true;
|
||||
ifaceNumber = uid->bInterfaceNumber;
|
||||
ifaceAltSet = uid->bAlternateSetting;
|
||||
protoValue = uid->bInterfaceProtocol;
|
||||
break;
|
||||
case USB_DESCRIPTOR_ENDPOINT:
|
||||
if(!valParser.Parse(pp, pcntdn))
|
||||
return false;
|
||||
if(isGoodInterface)
|
||||
if(theXtractor)
|
||||
theXtractor->EndpointXtract(confValue, ifaceNumber, ifaceAltSet, protoValue, (USB_ENDPOINT_DESCRIPTOR*)varBuffer);
|
||||
break;
|
||||
//case HID_DESCRIPTOR_HID:
|
||||
// if (!valParser.Parse(pp, pcntdn))
|
||||
|
|
@ -191,47 +180,44 @@ bool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor
|
|||
theBuffer.pValue = varBuffer;
|
||||
stateParseDescr = 0;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
|
||||
void ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc) {
|
||||
Notify(PSTR("\r\n\r\nHID Descriptor:\r\n"), 0x80);
|
||||
Notify(PSTR("bDescLength:\t\t"), 0x80);
|
||||
PrintHex<uint8_t > (pDesc->bLength, 0x80);
|
||||
Notify(PSTR("\r\n\r\nHID Descriptor:\r\n"), 0x80);
|
||||
Notify(PSTR("bDescLength:\t\t"), 0x80);
|
||||
PrintHex<uint8_t > (pDesc->bLength, 0x80);
|
||||
|
||||
Notify(PSTR("\r\nbDescriptorType:\t"), 0x80);
|
||||
PrintHex<uint8_t > (pDesc->bDescriptorType, 0x80);
|
||||
Notify(PSTR("\r\nbDescriptorType:\t"), 0x80);
|
||||
PrintHex<uint8_t > (pDesc->bDescriptorType, 0x80);
|
||||
|
||||
Notify(PSTR("\r\nbcdHID:\t\t\t"), 0x80);
|
||||
PrintHex<uint16_t > (pDesc->bcdHID, 0x80);
|
||||
Notify(PSTR("\r\nbcdHID:\t\t\t"), 0x80);
|
||||
PrintHex<uint16_t > (pDesc->bcdHID, 0x80);
|
||||
|
||||
Notify(PSTR("\r\nbCountryCode:\t\t"), 0x80);
|
||||
PrintHex<uint8_t > (pDesc->bCountryCode, 0x80);
|
||||
Notify(PSTR("\r\nbCountryCode:\t\t"), 0x80);
|
||||
PrintHex<uint8_t > (pDesc->bCountryCode, 0x80);
|
||||
|
||||
Notify(PSTR("\r\nbNumDescriptors:\t"), 0x80);
|
||||
PrintHex<uint8_t > (pDesc->bNumDescriptors, 0x80);
|
||||
Notify(PSTR("\r\nbNumDescriptors:\t"), 0x80);
|
||||
PrintHex<uint8_t > (pDesc->bNumDescriptors, 0x80);
|
||||
|
||||
//Notify(PSTR("\r\nbDescrType:\t\t"));
|
||||
//PrintHex<uint8_t>(pDesc->bDescrType);
|
||||
//
|
||||
//Notify(PSTR("\r\nwDescriptorLength:\t"));
|
||||
//PrintHex<uint16_t>(pDesc->wDescriptorLength);
|
||||
//Notify(PSTR("\r\nbDescrType:\t\t"));
|
||||
//PrintHex<uint8_t>(pDesc->bDescrType);
|
||||
//
|
||||
//Notify(PSTR("\r\nwDescriptorLength:\t"));
|
||||
//PrintHex<uint16_t>(pDesc->wDescriptorLength);
|
||||
|
||||
for (uint32_t i = 0; i < pDesc->bNumDescriptors; i++) {
|
||||
HID_CLASS_DESCRIPTOR_LEN_AND_TYPE *pLT = (HID_CLASS_DESCRIPTOR_LEN_AND_TYPE*)&(pDesc->bDescrType);
|
||||
|
||||
Notify(PSTR("\r\nbDescrType:\t\t"), 0x80);
|
||||
PrintHex<uint8_t > (pLT[i].bDescrType, 0x80);
|
||||
Notify(PSTR("\r\nbDescrType:\t\t"), 0x80);
|
||||
PrintHex<uint8_t > (pLT[i].bDescrType, 0x80);
|
||||
|
||||
Notify(PSTR("\r\nwDescriptorLength:\t"), 0x80);
|
||||
PrintHex<uint16_t > (pLT[i].wDescriptorLength, 0x80);
|
||||
}
|
||||
Notify(PSTR("\r\n"), 0x80);
|
||||
Notify(PSTR("\r\nwDescriptorLength:\t"), 0x80);
|
||||
PrintHex<uint16_t > (pLT[i].wDescriptorLength, 0x80);
|
||||
}
|
||||
Notify(PSTR("\r\n"), 0x80);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // __CONFDESCPARSER_H__
|
||||
|
|
|
|||
|
|
@ -994,6 +994,7 @@ void ReportDescParserBase::Parse(const uint32_t len, const uint8_t *pbuf, const
|
|||
uint32_t cntdn = (uint32_t)len;
|
||||
uint8_t *p = (uint8_t*)pbuf;
|
||||
|
||||
|
||||
totalSize = 0;
|
||||
|
||||
while(cntdn) {
|
||||
|
|
@ -1090,17 +1091,6 @@ void ReportDescParserBase::PrintItemTitle(uint8_t prefix) {
|
|||
uint8_t ReportDescParserBase::ParseItem(uint8_t **pp, uint32_t *pcntdn) {
|
||||
//uint8_t ret = enErrorSuccess;
|
||||
//reinterpret_cast<>(varBuffer);
|
||||
|
||||
|
||||
#pragma GCC diagnostic push // Available since GCC 4.6.4
|
||||
/*
|
||||
* FIXME -- Enabled and review all `-Wimplicit-fallthrough` messages
|
||||
* This code has multiple switch statements that "fall through" to the
|
||||
* next case -- but it's not always clear if this is intentional or not.
|
||||
* Review and commenting of code, and reducing cyclomatic complexity
|
||||
* are highly recommended....
|
||||
*/
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
switch(itemParseState) {
|
||||
case 0:
|
||||
if(**pp == HID_LONG_ITEM_PREFIX)
|
||||
|
|
@ -1217,7 +1207,6 @@ uint8_t ReportDescParserBase::ParseItem(uint8_t **pp, uint32_t *pcntdn) {
|
|||
} // switch (**pp & (TYPE_MASK | TAG_MASK))
|
||||
}
|
||||
} // switch (itemParseState)
|
||||
#pragma GCC diagnostic pop
|
||||
itemParseState = 0;
|
||||
return enErrorSuccess;
|
||||
}
|
||||
|
|
@ -1247,18 +1236,18 @@ void ReportDescParserBase::SetUsagePage(uint16_t page) {
|
|||
if(VALUE_BETWEEN(page, 0x00, 0x11))
|
||||
pfUsage = (usagePageFunctions[page - 1]);
|
||||
|
||||
// Dead code...
|
||||
//
|
||||
// pfUsage = (UsagePageFunc)pgm_read_pointer(usagePageFunctions[page - 1]);
|
||||
//else if (page > 0x7f && page < 0x84)
|
||||
// E_Notify(pstrUsagePageMonitor);
|
||||
//else if (page > 0x83 && page < 0x8c)
|
||||
// E_Notify(pstrUsagePagePower);
|
||||
//else if (page > 0x8b && page < 0x92)
|
||||
// E_Notify((char*)pgm_read_pointer(&usagePageTitles1[page - 0x8c]));
|
||||
//else if (page > 0xfeff && page <= 0xffff)
|
||||
// E_Notify(pstrUsagePageVendorDefined);
|
||||
//
|
||||
// Dead code...
|
||||
//
|
||||
// pfUsage = (UsagePageFunc)pgm_read_pointer(usagePageFunctions[page - 1]);
|
||||
//else if (page > 0x7f && page < 0x84)
|
||||
// E_Notify(pstrUsagePageMonitor);
|
||||
//else if (page > 0x83 && page < 0x8c)
|
||||
// E_Notify(pstrUsagePagePower);
|
||||
//else if (page > 0x8b && page < 0x92)
|
||||
// E_Notify((char*)pgm_read_pointer(&usagePageTitles1[page - 0x8c]));
|
||||
//else if (page > 0xfeff && page <= 0xffff)
|
||||
// E_Notify(pstrUsagePageVendorDefined);
|
||||
//
|
||||
else
|
||||
switch(page) {
|
||||
case 0x14:
|
||||
|
|
@ -1451,15 +1440,6 @@ void ReportDescParserBase::PrintMedicalInstrumentPageUsage(uint16_t usage) {
|
|||
uint8_t ReportDescParser2::ParseItem(uint8_t **pp, uint32_t *pcntdn) {
|
||||
//uint8_t ret = enErrorSuccess;
|
||||
|
||||
#pragma GCC diagnostic push // Available since GCC 4.6.4
|
||||
/*
|
||||
* FIXME -- Enabled and review all `-Wimplicit-fallthrough` messages
|
||||
* This code has multiple switch statements that "fall through" to the
|
||||
* next case -- but it's not always clear if this is intentional or not.
|
||||
* Review and commenting of code, and reducing cyclomatic complexity
|
||||
* are highly recommended....
|
||||
*/
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
switch(itemParseState) {
|
||||
case 0:
|
||||
if(**pp == HID_LONG_ITEM_PREFIX)
|
||||
|
|
@ -1539,8 +1519,6 @@ uint8_t ReportDescParser2::ParseItem(uint8_t **pp, uint32_t *pcntdn) {
|
|||
} // switch (**pp & (TYPE_MASK | TAG_MASK))
|
||||
}
|
||||
} // switch (itemParseState)
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
itemParseState = 0;
|
||||
return enErrorSuccess;
|
||||
}
|
||||
|
|
@ -1580,7 +1558,8 @@ void ReportDescParser2::OnInputItem(uint8_t itm) {
|
|||
// bits_to_copy - number of bits to copy to result buffer
|
||||
|
||||
// for each bit in a field
|
||||
for(uint8_t bits_left = rptSize, bits_to_copy = 0; bits_left; bits_left -= bits_to_copy) {
|
||||
for(uint8_t bits_left = rptSize, bits_to_copy = 0; bits_left;
|
||||
bits_left -= bits_to_copy) {
|
||||
bits_to_copy = (bits_left > bits_of_byte) ? bits_of_byte : bits_left;
|
||||
|
||||
result.dwResult <<= bits_to_copy; // Result buffer is shifted by the number of bits to be copied into it
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ e-mail : support@circuitsathome.com
|
|||
#include "Usb.h"
|
||||
|
||||
bool MultiByteValueParser::Parse(uint8_t **pp, uint32_t *pcntdn) {
|
||||
if(!pBuf) {
|
||||
Notify(PSTR("Buffer pointer is NULL!\r\n"), 0x80);
|
||||
if(!pBuf) {
|
||||
Notify(PSTR("Buffer pointer is NULL!\r\n"), 0x80);
|
||||
return false;
|
||||
}
|
||||
for (; countDown && (*pcntdn); countDown--, (*pcntdn)--, (*pp)++)
|
||||
pBuf[valueSize - countDown] = (**pp);
|
||||
|
||||
if(countDown)
|
||||
if(countDown)
|
||||
return false;
|
||||
|
||||
countDown = valueSize;
|
||||
|
|
@ -32,48 +32,36 @@ bool MultiByteValueParser::Parse(uint8_t **pp, uint32_t *pcntdn) {
|
|||
}
|
||||
|
||||
bool PTPListParser::Parse(uint8_t **pp, uint32_t *pcntdn, PTP_ARRAY_EL_FUNC pf, const void *me) {
|
||||
switch(nStage) {
|
||||
case 0:
|
||||
pBuf->valueSize = lenSize;
|
||||
theParser.Initialize(pBuf);
|
||||
nStage = 1;
|
||||
|
||||
#pragma GCC diagnostic push // Available since GCC 4.6.4
|
||||
/*
|
||||
* FIXME -- Enabled and review all `-Wimplicit-fallthrough` messages
|
||||
* This code has multiple switch statements that "fall through" to the
|
||||
* next case -- but it's not always clear if this is intentional or not.
|
||||
* Review and commenting of code, and reducing cyclomatic complexity
|
||||
* are highly recommended....
|
||||
*/
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
case 1:
|
||||
if(!theParser.Parse(pp, pcntdn))
|
||||
return false;
|
||||
|
||||
switch(nStage) {
|
||||
case 0:
|
||||
pBuf->valueSize = lenSize;
|
||||
theParser.Initialize(pBuf);
|
||||
nStage = 1;
|
||||
arLen = 0;
|
||||
arLen = (pBuf->valueSize >= 4) ? *((uint32_t*)pBuf->pValue) : (uint32_t)(*((uint16_t*)pBuf->pValue));
|
||||
arLenCntdn = arLen;
|
||||
nStage = 2;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
pBuf->valueSize = valSize;
|
||||
theParser.Initialize(pBuf);
|
||||
nStage = 3;
|
||||
|
||||
case 3:
|
||||
for(; arLenCntdn; arLenCntdn--) {
|
||||
if(!theParser.Parse(pp, pcntdn))
|
||||
return false;
|
||||
|
||||
arLen = 0;
|
||||
arLen = (pBuf->valueSize >= 4) ? *((uint32_t*)pBuf->pValue) : (uint32_t)(*((uint16_t*)pBuf->pValue));
|
||||
arLenCntdn = arLen;
|
||||
nStage = 2;
|
||||
|
||||
case 2:
|
||||
pBuf->valueSize = valSize;
|
||||
theParser.Initialize(pBuf);
|
||||
nStage = 3;
|
||||
|
||||
case 3:
|
||||
for(; arLenCntdn; arLenCntdn--) {
|
||||
if(!theParser.Parse(pp, pcntdn))
|
||||
return false;
|
||||
|
||||
if(pf)
|
||||
pf(pBuf, (arLen - arLenCntdn), me);
|
||||
}
|
||||
|
||||
nStage = 0;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,12 +35,12 @@ class MultiByteValueParser {
|
|||
|
||||
public:
|
||||
|
||||
MultiByteValueParser() : pBuf(NULL), countDown(0), valueSize(0) {
|
||||
};
|
||||
MultiByteValueParser() : pBuf(NULL), countDown(0), valueSize(0) {
|
||||
};
|
||||
|
||||
const uint8_t* GetBuffer() {
|
||||
return pBuf;
|
||||
};
|
||||
const uint8_t* GetBuffer() {
|
||||
return pBuf;
|
||||
};
|
||||
|
||||
void Initialize(MultiValueBuffer * const pbuf) {
|
||||
pBuf = (uint8_t*)pbuf->pValue;
|
||||
|
|
@ -58,7 +58,7 @@ class ByteSkipper {
|
|||
public:
|
||||
|
||||
ByteSkipper() : pBuf(NULL), nStage(0), countDown(0) {
|
||||
};
|
||||
};
|
||||
|
||||
void Initialize(MultiValueBuffer *pbuf) {
|
||||
pBuf = (uint8_t*)pbuf->pValue;
|
||||
|
|
@ -66,25 +66,16 @@ public:
|
|||
};
|
||||
|
||||
bool Skip(uint8_t **pp, uint32_t *pcntdn, uint32_t bytes_to_skip) {
|
||||
#pragma GCC diagnostic push // Available since GCC 4.6.4
|
||||
/*
|
||||
* FIXME -- Enabled and review all `-Wimplicit-fallthrough` messages
|
||||
* This code has multiple switch statements that "fall through" to the
|
||||
* next case -- but it's not always clear if this is intentional or not.
|
||||
* Review and commenting of code, and reducing cyclomatic complexity
|
||||
* are highly recommended....
|
||||
*/
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
switch(nStage) {
|
||||
case 0:
|
||||
countDown = bytes_to_skip;
|
||||
nStage++;
|
||||
case 1:
|
||||
for(; countDown && (*pcntdn); countDown--, (*pp)++, (*pcntdn)--);
|
||||
if(!countDown)
|
||||
nStage = 0;
|
||||
switch(nStage) {
|
||||
case 0:
|
||||
countDown = bytes_to_skip;
|
||||
nStage++;
|
||||
case 1:
|
||||
for(; countDown && (*pcntdn); countDown--, (*pp)++, (*pcntdn)--);
|
||||
|
||||
if(!countDown)
|
||||
nStage = 0;
|
||||
};
|
||||
#pragma GCC diagnostic pop
|
||||
return (!countDown);
|
||||
};
|
||||
};
|
||||
|
|
@ -95,9 +86,9 @@ typedef void (*PTP_ARRAY_EL_FUNC)(const MultiValueBuffer * const p, uint32_t cou
|
|||
class PTPListParser {
|
||||
public:
|
||||
|
||||
enum ParseMode {
|
||||
modeArray, modeRange/*, modeEnum*/
|
||||
};
|
||||
enum ParseMode {
|
||||
modeArray, modeRange/*, modeEnum*/
|
||||
};
|
||||
|
||||
private:
|
||||
uint32_t nStage;
|
||||
|
|
|
|||
|
|
@ -287,13 +287,6 @@ void TwoWire::onService(void)
|
|||
void WIRE_IT_HANDLER(void) {
|
||||
Wire.onService();
|
||||
}
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
void WIRE_IT_HANDLER_0(void) { Wire.onService(); }
|
||||
void WIRE_IT_HANDLER_1(void) { Wire.onService(); }
|
||||
void WIRE_IT_HANDLER_2(void) { Wire.onService(); }
|
||||
void WIRE_IT_HANDLER_3(void) { Wire.onService(); }
|
||||
#endif // __SAMD51__
|
||||
#endif
|
||||
|
||||
#if WIRE_INTERFACES_COUNT > 1
|
||||
|
|
@ -302,13 +295,6 @@ void TwoWire::onService(void)
|
|||
void WIRE1_IT_HANDLER(void) {
|
||||
Wire1.onService();
|
||||
}
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
void WIRE1_IT_HANDLER_0(void) { Wire1.onService(); }
|
||||
void WIRE1_IT_HANDLER_1(void) { Wire1.onService(); }
|
||||
void WIRE1_IT_HANDLER_2(void) { Wire1.onService(); }
|
||||
void WIRE1_IT_HANDLER_3(void) { Wire1.onService(); }
|
||||
#endif // __SAMD51__
|
||||
#endif
|
||||
|
||||
#if WIRE_INTERFACES_COUNT > 2
|
||||
|
|
@ -317,13 +303,6 @@ void TwoWire::onService(void)
|
|||
void WIRE2_IT_HANDLER(void) {
|
||||
Wire2.onService();
|
||||
}
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
void WIRE2_IT_HANDLER_0(void) { Wire2.onService(); }
|
||||
void WIRE2_IT_HANDLER_1(void) { Wire2.onService(); }
|
||||
void WIRE2_IT_HANDLER_2(void) { Wire2.onService(); }
|
||||
void WIRE2_IT_HANDLER_3(void) { Wire2.onService(); }
|
||||
#endif // __SAMD51__
|
||||
#endif
|
||||
|
||||
#if WIRE_INTERFACES_COUNT > 3
|
||||
|
|
@ -332,13 +311,6 @@ void TwoWire::onService(void)
|
|||
void WIRE3_IT_HANDLER(void) {
|
||||
Wire3.onService();
|
||||
}
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
void WIRE3_IT_HANDLER_0(void) { Wire3.onService(); }
|
||||
void WIRE3_IT_HANDLER_1(void) { Wire3.onService(); }
|
||||
void WIRE3_IT_HANDLER_2(void) { Wire3.onService(); }
|
||||
void WIRE3_IT_HANDLER_3(void) { Wire3.onService(); }
|
||||
#endif // __SAMD51__
|
||||
#endif
|
||||
|
||||
#if WIRE_INTERFACES_COUNT > 4
|
||||
|
|
@ -347,13 +319,6 @@ void TwoWire::onService(void)
|
|||
void WIRE4_IT_HANDLER(void) {
|
||||
Wire4.onService();
|
||||
}
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
void WIRE4_IT_HANDLER_0(void) { Wire4.onService(); }
|
||||
void WIRE4_IT_HANDLER_1(void) { Wire4.onService(); }
|
||||
void WIRE4_IT_HANDLER_2(void) { Wire4.onService(); }
|
||||
void WIRE4_IT_HANDLER_3(void) { Wire4.onService(); }
|
||||
#endif // __SAMD51__
|
||||
#endif
|
||||
|
||||
#if WIRE_INTERFACES_COUNT > 5
|
||||
|
|
@ -362,12 +327,5 @@ void TwoWire::onService(void)
|
|||
void WIRE5_IT_HANDLER(void) {
|
||||
Wire5.onService();
|
||||
}
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
void WIRE5_IT_HANDLER_0(void) { Wire5.onService(); }
|
||||
void WIRE5_IT_HANDLER_1(void) { Wire5.onService(); }
|
||||
void WIRE5_IT_HANDLER_2(void) { Wire5.onService(); }
|
||||
void WIRE5_IT_HANDLER_3(void) { Wire5.onService(); }
|
||||
#endif // __SAMD51__
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ void loop()
|
|||
// this function is registered as an event, see setup()
|
||||
void receiveEvent(int howMany)
|
||||
{
|
||||
(void)howMany; // avoid compiler warning about unused parameter
|
||||
|
||||
while(1 < Wire.available()) // loop through all but the last
|
||||
{
|
||||
char c = Wire.read(); // receive byte as a character
|
||||
|
|
|
|||
90
platform.txt
90
platform.txt
|
|
@ -20,7 +20,7 @@
|
|||
# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification
|
||||
|
||||
name=Adafruit SAMD (32-bits ARM Cortex-M0+ and Cortex-M4) Boards
|
||||
version=1.6.3
|
||||
version=1.4.1
|
||||
|
||||
# Compile variables
|
||||
# -----------------
|
||||
|
|
@ -28,13 +28,13 @@ version=1.6.3
|
|||
compiler.warning_flags=-w
|
||||
compiler.warning_flags.none=-w
|
||||
compiler.warning_flags.default=
|
||||
compiler.warning_flags.more=-Wall -Wno-expansion-to-defined
|
||||
compiler.warning_flags.all=-Wall -Wextra -Wno-expansion-to-defined
|
||||
compiler.warning_flags.more=-Wall
|
||||
compiler.warning_flags.all=-Wall -Wextra
|
||||
|
||||
compiler.path={runtime.tools.arm-none-eabi-gcc.path}/bin/
|
||||
compiler.c.cmd=arm-none-eabi-gcc
|
||||
compiler.c.flags=-mcpu={build.mcu} -mthumb -c -g -Os {compiler.warning_flags} -std=gnu11 -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500 -MMD -D__SKETCH_NAME__="""{build.project_name}"""
|
||||
compiler.c.elf.cmd=arm-none-eabi-g++
|
||||
compiler.c.elf.cmd=arm-none-eabi-gcc
|
||||
compiler.c.elf.flags=-Os -Wl,--gc-sections -save-temps
|
||||
compiler.S.cmd=arm-none-eabi-gcc
|
||||
compiler.S.flags=-c -g -x assembler-with-cpp -MMD
|
||||
|
|
@ -44,13 +44,11 @@ compiler.ar.cmd=arm-none-eabi-ar
|
|||
compiler.ar.flags=rcs
|
||||
compiler.objcopy.cmd=arm-none-eabi-objcopy
|
||||
compiler.objcopy.eep.flags=-O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0
|
||||
compiler.elf2hex.bin.flags=-O binary
|
||||
compiler.elf2hex.hex.flags=-O ihex -R .eeprom
|
||||
compiler.elf2hex.flags=-O binary
|
||||
compiler.elf2hex.cmd=arm-none-eabi-objcopy
|
||||
compiler.ldflags=-mcpu={build.mcu} -mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align
|
||||
compiler.size.cmd=arm-none-eabi-size
|
||||
compiler.define=-DARDUINO=
|
||||
compiler.readelf.cmd=arm-none-eabi-readelf
|
||||
|
||||
# this can be overriden in boards.txt
|
||||
build.extra_flags=
|
||||
|
|
@ -58,8 +56,6 @@ build.cache_flags=
|
|||
build.flags.optimize=
|
||||
build.flags.maxspi=
|
||||
build.flags.maxqspi=
|
||||
build.flags.usbstack=
|
||||
build.flags.debug=
|
||||
|
||||
# These can be overridden in platform.local.txt
|
||||
compiler.c.extra_flags=
|
||||
|
|
@ -70,17 +66,12 @@ compiler.S.extra_flags=
|
|||
compiler.ar.extra_flags=
|
||||
compiler.elf2hex.extra_flags=
|
||||
|
||||
compiler.arm.cmsis.c.flags="-I{runtime.tools.CMSIS-5.4.0.path}/CMSIS/Core/Include/" "-I{runtime.tools.CMSIS-5.4.0.path}/CMSIS/DSP/Include/" "-I{runtime.tools.CMSIS-Atmel-1.2.0.path}/CMSIS/Device/ATMEL/"
|
||||
compiler.arm.cmsis.ldflags="-L{runtime.tools.CMSIS-5.4.0.path}/CMSIS/Lib/GCC/" -larm_cortexM0l_math
|
||||
|
||||
compiler.libraries.ldflags=
|
||||
compiler.arm.cmsis.c.flags="-I{runtime.tools.CMSIS-4.5.0.path}/CMSIS/Include/" "-I{runtime.tools.CMSIS-Atmel-1.2.0.path}/CMSIS/Device/ATMEL/"
|
||||
compiler.arm.cmsis.ldflags="-L{runtime.tools.CMSIS-4.5.0.path}/CMSIS/Lib/GCC/" -larm_cortexM0l_math
|
||||
|
||||
# USB Flags
|
||||
# ---------
|
||||
build.usb_flags=-DUSB_VID={build.vid} -DUSB_PID={build.pid} -DUSBCON -DUSB_CONFIG_POWER={build.usb_power} '-DUSB_MANUFACTURER={build.usb_manufacturer}' '-DUSB_PRODUCT={build.usb_product}' {build.flags.usbstack} {build.flags.debug} "-I{build.core.path}/TinyUSB" "-I{build.core.path}/TinyUSB/Adafruit_TinyUSB_ArduinoCore" "-I{build.core.path}/TinyUSB/Adafruit_TinyUSB_ArduinoCore/tinyusb/src"
|
||||
|
||||
# Default advertised device power setting in mA
|
||||
build.usb_power=100
|
||||
build.usb_flags=-DUSB_VID={build.vid} -DUSB_PID={build.pid} -DUSBCON '-DUSB_MANUFACTURER={build.usb_manufacturer}' '-DUSB_PRODUCT={build.usb_product}'
|
||||
|
||||
# Default usb manufacturer will be replaced at compile time using
|
||||
# numeric vendor ID if available or by board's specific value.
|
||||
|
|
@ -91,44 +82,41 @@ build.usb_manufacturer="Unknown"
|
|||
# ----------------
|
||||
|
||||
## Compile c files
|
||||
recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {build.cache_flags} {build.flags.debug} {build.flags.optimize} {build.flags.maxspi} {build.flags.maxqspi} {compiler.arm.cmsis.c.flags} {includes} "{source_file}" -o "{object_file}"
|
||||
recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {build.cache_flags} {build.flags.optimize} {build.flags.maxspi} {build.flags.maxqspi} {compiler.arm.cmsis.c.flags} {includes} "{source_file}" -o "{object_file}"
|
||||
|
||||
## Compile c++ files
|
||||
recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {build.cache_flags} {build.flags.debug} {build.flags.optimize} {build.flags.maxspi} {build.flags.maxqspi} {build.extra_flags} {compiler.arm.cmsis.c.flags} {includes} "{source_file}" -o "{object_file}"
|
||||
recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {build.cache_flags} {build.flags.optimize} {build.flags.maxspi} {build.flags.maxqspi} {compiler.arm.cmsis.c.flags} {includes} "{source_file}" -o "{object_file}"
|
||||
|
||||
## Compile S files
|
||||
recipe.S.o.pattern="{compiler.path}{compiler.S.cmd}" {compiler.S.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.S.extra_flags} {build.extra_flags} {build.cache_flags} {compiler.arm.cmsis.c.flags} {includes} "{source_file}" -o "{object_file}"
|
||||
|
||||
## Create archives
|
||||
# archive_file_path is needed for backwards compatibility with IDE 1.6.5 or older, IDE 1.6.6 or newer overrides this value
|
||||
archive_file_path={build.path}/{archive_file}
|
||||
recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}"
|
||||
|
||||
## Combine gc-sections, archives, and objects
|
||||
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" "-L{build.path}" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" --specs=nano.specs --specs=nosys.specs {compiler.ldflags} -o "{build.path}/{build.project_name}.elf" {object_files} {compiler.libraries.ldflags} -Wl,--start-group {compiler.arm.cmsis.ldflags} "-L{build.variant.path}" -lm "{build.path}/{archive_file}" -Wl,--end-group
|
||||
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" "-L{build.path}" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" --specs=nano.specs --specs=nosys.specs {compiler.ldflags} -o "{build.path}/{build.project_name}.elf" {object_files} -Wl,--start-group {compiler.arm.cmsis.ldflags} "-L{build.variant.path}" -lm "{build.path}/{archive_file}" -Wl,--end-group
|
||||
|
||||
## Create output (bin file)
|
||||
recipe.objcopy.bin.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.bin.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin"
|
||||
|
||||
## Create output (hex file)
|
||||
recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex"
|
||||
|
||||
build.preferred_out_format=bin
|
||||
recipe.objcopy.bin.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin"
|
||||
|
||||
## Save hex
|
||||
recipe.output.tmp_file={build.project_name}.{build.preferred_out_format}
|
||||
recipe.output.save_file={build.project_name}.{build.variant}.{build.preferred_out_format}
|
||||
recipe.output.tmp_file={build.project_name}.bin
|
||||
recipe.output.save_file={build.project_name}.{build.variant}.bin
|
||||
|
||||
## Compute size
|
||||
recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf"
|
||||
recipe.size.regex=\.text\s+([0-9]+).*
|
||||
|
||||
|
||||
# Uploader tools
|
||||
# --------------
|
||||
|
||||
#
|
||||
# BOSSA
|
||||
#
|
||||
tools.bossac.path={runtime.tools.bossac-1.7.0-arduino3.path}
|
||||
|
||||
tools.bossac.path={runtime.tools.bossac-1.7.0.path}
|
||||
tools.bossac.cmd=bossac
|
||||
tools.bossac.cmd.windows=bossac.exe
|
||||
|
||||
tools.bossac.upload.params.verbose=-i -d
|
||||
tools.bossac.upload.params.quiet=
|
||||
|
|
@ -151,68 +139,48 @@ tools.bossac18.upload.pattern="{path}/{cmd}" {upload.verbose} --port={serial.por
|
|||
tools.bossac18.network_cmd={runtime.tools.arduinoOTA.path}/bin/arduinoOTA
|
||||
tools.bossac18.upload.network_pattern="{network_cmd}" -address {serial.port} -port 65280 -username arduino -password "{network.password}" -sketch "{build.path}/{build.project_name}.bin" -upload /sketch -b
|
||||
|
||||
#
|
||||
# BOSSA (ignore binary size)
|
||||
#
|
||||
tools.bossacI.path={runtime.tools.bossac-1.7.0-arduino3.path}
|
||||
tools.bossacI.cmd=bossac
|
||||
tools.bossacI.cmd.windows=bossac.exe
|
||||
|
||||
tools.bossacI.upload.params.verbose=-i -d
|
||||
tools.bossacI.upload.params.quiet=
|
||||
tools.bossacI.upload.pattern="{path}/{cmd}" {upload.verbose} --port={serial.port.file} -I -U {upload.native_usb} -i -e -w "{build.path}/{build.project_name}.bin" -R
|
||||
|
||||
tools.bossacI_remote.upload.pattern=/usr/bin/run-bossac {upload.verbose} --port=ttyATH0 -U {upload.native_usb} -e -w -v /tmp/sketch.bin -R
|
||||
|
||||
tools.bossacI.network_cmd={runtime.tools.arduinoOTA.path}/bin/arduinoOTA
|
||||
tools.bossacI.upload.network_pattern="{network_cmd}" -address {serial.port} -port 65280 -username arduino -password "{network.password}" -sketch "{build.path}/{build.project_name}.bin" -upload /sketch -b
|
||||
|
||||
|
||||
#
|
||||
# OpenOCD sketch upload
|
||||
#
|
||||
|
||||
tools.openocd.path={runtime.tools.openocd-0.10.0-arduino7.path}
|
||||
tools.openocd.path={runtime.tools.openocd.path}
|
||||
tools.openocd.cmd=bin/openocd
|
||||
tools.openocd.cmd.windows=bin/openocd.exe
|
||||
|
||||
tools.openocd.upload.params.verbose=-d2
|
||||
tools.openocd.upload.params.quiet=-d0
|
||||
tools.openocd.upload.pattern="{path}/{cmd}" {upload.verbose} -s "{path}/share/openocd/scripts/" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; program {{build.path}/{build.project_name}.bin} verify reset 0x2000; shutdown"
|
||||
tools.openocd.upload.pattern="{path}/{cmd}" {upload.verbose} -s "{path}/share/openocd/scripts/" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; program {{{build.path}/{build.project_name}.bin}} verify reset 0x00002000; shutdown"
|
||||
|
||||
tools.openocd.network_cmd={runtime.tools.arduinoOTA.path}/bin/arduinoOTA
|
||||
tools.openocd.upload.network_pattern={network_cmd} -address {serial.port} -port 65280 -username arduino -password "{network.password}" -sketch "{build.path}/{build.project_name}.bin" -upload /sketch -b
|
||||
|
||||
# Program flashes the binary at 0x0000, so use the linker script without_bootloader
|
||||
tools.openocd.program.params.verbose=-d2
|
||||
tools.openocd.program.params.quiet=-d0
|
||||
tools.openocd.program.pattern="{path}/{cmd}" {program.verbose} -s "{path}/share/openocd/scripts/" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; program {{build.path}/{build.project_name}.elf} verify reset; shutdown"
|
||||
tools.openocd.program.pattern="{path}/{cmd}" {program.verbose} -s "{path}/share/openocd/scripts/" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; program {{{build.path}/{build.project_name}.elf}} verify reset; shutdown"
|
||||
|
||||
tools.openocd.erase.params.verbose=-d3
|
||||
tools.openocd.erase.params.quiet=-d0
|
||||
tools.openocd.erase.pattern=
|
||||
|
||||
tools.openocd.bootloader.params.verbose=-d2
|
||||
tools.openocd.bootloader.params.verbose=-d3
|
||||
tools.openocd.bootloader.params.quiet=-d0
|
||||
tools.openocd.bootloader.pattern="{path}/{cmd}" {bootloader.verbose} -s "{path}/share/openocd/scripts/" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; init; halt; at91samd bootloader 0; program {{runtime.platform.path}/bootloaders/{bootloader.file}} verify reset; shutdown"
|
||||
tools.openocd.bootloader.pattern="{path}/{cmd}" {bootloader.verbose} -s "{path}/share/openocd/scripts/" -f interface/{program.protocol}.cfg -c "{program.setup_command}" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; init; halt; at91samd bootloader 0; program {{{runtime.platform.path}/bootloaders/{bootloader.file}}} verify reset; shutdown"
|
||||
|
||||
#
|
||||
# OpenOCD sketch upload - version with configurable bootloader size
|
||||
# FIXME: this programmer is a workaround for default options being overwritten by uploadUsingPreferences
|
||||
#
|
||||
|
||||
tools.openocd-withbootsize.path={runtime.tools.openocd-0.10.0-arduino7.path}
|
||||
tools.openocd-withbootsize.path={runtime.tools.openocd-0.9.0-arduino.path}
|
||||
tools.openocd-withbootsize.cmd=bin/openocd
|
||||
tools.openocd-withbootsize.cmd.windows=bin/openocd.exe
|
||||
|
||||
tools.openocd-withbootsize.upload.params.verbose=-d2
|
||||
tools.openocd-withbootsize.upload.params.quiet=-d0
|
||||
tools.openocd-withbootsize.upload.pattern="{path}/{cmd}" {upload.verbose} -s "{path}/share/openocd/scripts/" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; program {{build.path}/{build.project_name}.bin} verify reset {bootloader.size}; shutdown"
|
||||
tools.openocd-withbootsize.upload.pattern="{path}/{cmd}" {upload.verbose} -s "{path}/share/openocd/scripts/" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; program {{{build.path}/{build.project_name}.bin}} verify reset {bootloader.size}; shutdown"
|
||||
|
||||
# Program flashes the binary at 0x0000, so use the linker script without_bootloader
|
||||
tools.openocd-withbootsize.program.params.verbose=-d2
|
||||
tools.openocd-withbootsize.program.params.quiet=-d0
|
||||
tools.openocd-withbootsize.program.pattern="{path}/{cmd}" {program.verbose} -s "{path}/share/openocd/scripts/" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; program {{build.path}/{build.project_name}.elf} verify reset; shutdown"
|
||||
tools.openocd-withbootsize.program.pattern="{path}/{cmd}" {program.verbose} -s "{path}/share/openocd/scripts/" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; program {{{build.path}/{build.project_name}.elf}} verify reset; shutdown"
|
||||
|
||||
tools.openocd-withbootsize.erase.params.verbose=-d3
|
||||
tools.openocd-withbootsize.erase.params.quiet=-d0
|
||||
|
|
@ -220,4 +188,4 @@ tools.openocd-withbootsize.erase.pattern=
|
|||
|
||||
tools.openocd-withbootsize.bootloader.params.verbose=-d2
|
||||
tools.openocd-withbootsize.bootloader.params.quiet=-d0
|
||||
tools.openocd-withbootsize.bootloader.pattern="{path}/{cmd}" {bootloader.verbose} -s "{path}/share/openocd/scripts/" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; init; halt; at91samd bootloader 0; program {{runtime.platform.path}/bootloaders/{bootloader.file}} verify reset; shutdown"
|
||||
tools.openocd-withbootsize.bootloader.pattern="{path}/{cmd}" {bootloader.verbose} -s "{path}/share/openocd/scripts/" -f "{runtime.platform.path}/variants/{build.variant}/{build.openocdscript}" -c "telnet_port disabled; init; halt; at91samd bootloader 0; program {{{runtime.platform.path}/bootloaders/{bootloader.file}}} verify reset; shutdown"
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
#
|
||||
# Arduino Zero OpenOCD script.
|
||||
#
|
||||
# Copyright (c) 2014-2015 Arduino LLC. All right reserved.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
|
||||
# Define 'reset' command
|
||||
define reset
|
||||
|
||||
info reg
|
||||
|
||||
break main
|
||||
|
||||
# End of 'reset' command
|
||||
end
|
||||
|
||||
target remote | openocd -c "interface cmsis-dap" -c "set CHIPNAME at91samd21g18" -f target/at91samdXX.cfg -c "gdb_port pipe; log_output openocd.log"
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue