diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c622a43..7c09a06 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,21 @@ -## Contributing to STM32_PWM +## Contributing to STM32_Slow_PWM ### Reporting Bugs -Please report bugs in [STM32_PWM Issues](https://github.com/khoih-prog/STM32_PWM/issues) if you find them. +Please report bugs in STM32_TimerInterrupt if you find them. However, before reporting a bug please check through the following: -* [Existing Open Issues](https://github.com/khoih-prog/STM32_PWM/issues) - someone might have already encountered this. +* [Existing Open Issues](https://github.com/khoih-prog/STM32_TimerInterrupt/issues) - someone might have already encountered this. -If you don't find anything, please [open a new issue](https://github.com/khoih-prog/STM32_PWM/issues/new). +If you don't find anything, please [open a new issue](https://github.com/khoih-prog/STM32_TimerInterrupt/issues/new). ### How to submit a bug report Please ensure to specify the following: -* Arduino IDE version (e.g. 1.8.16) or Platform.io version -* `STM32` Core Version (e.g. STM32 core v2.1.0) +* Arduino IDE version (e.g. 1.8.19) or Platform.io version +* `STM32` Core Version (e.g. STM32 core v2.2.0) * Board type and relevant info * Contextual information (e.g. what you were trying to achieve) * Simplest possible steps to reproduce @@ -27,11 +27,11 @@ Please ensure to specify the following: ### Example ``` -Arduino IDE version: 1.8.16 -STM32 Core Version 2.1.0 +Arduino IDE version: 1.8.19 +STM32 Core Version 2.2.0 Nucleo-144 STM32H7 NUCLEO_H743ZI2 OS: Ubuntu 20.04 LTS -Linux xy-Inspiron-3593 5.4.0-86-generic #97-Ubuntu SMP Fri Sep 17 19:19:40 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux +Linux xy-Inspiron-3593 5.4.0-100-generic #113-Ubuntu SMP Thu Feb 3 18:43:29 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Context: I encountered a crash while using TimerInterrupt. @@ -47,7 +47,7 @@ Steps to reproduce: Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. -There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/STM32_PWM/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. +There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/STM32_Slow_PWM/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. ### Sending Pull Requests diff --git a/README.md b/README.md index ae18119..9e72715 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,52 @@ -# STM32_PWM Library +# STM32_Slow_PWM Library -[![arduino-library-badge](https://www.ardu-badge.com/badge/STM32_PWM.svg?)](https://www.ardu-badge.com/STM32_PWM) -[![GitHub release](https://img.shields.io/github/release/khoih-prog/STM32_PWM.svg)](https://github.com/khoih-prog/STM32_PWM/releases) -[![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/STM32_PWM/blob/master/LICENSE) +[![arduino-library-badge](https://www.ardu-badge.com/badge/STM32_Slow_PWM.svg?)](https://www.ardu-badge.com/STM32_Slow_PWM) +[![GitHub release](https://img.shields.io/github/release/khoih-prog/STM32_Slow_PWM.svg)](https://github.com/khoih-prog/STM32_Slow_PWM/releases) +[![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/STM32_Slow_PWM/blob/master/LICENSE) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) -[![GitHub issues](https://img.shields.io/github/issues/khoih-prog/STM32_PWM.svg)](http://github.com/khoih-prog/STM32_PWM/issues) - -Buy Me A Coffee +[![GitHub issues](https://img.shields.io/github/issues/khoih-prog/STM32_Slow_PWM.svg)](http://github.com/khoih-prog/STM32_Slow_PWM/issues) +Donate to my libraries using BuyMeACoffee + --- --- ## Table of Contents -* [Why do we need this STM32_PWM library](#why-do-we-need-this-STM32_PWM-library) +* [Important Change from v1.2.0](#Important-Change-from-v120) +* [Why do we need this STM32_Slow_PWM library](#why-do-we-need-this-STM32_Slow_PWM-library) * [Features](#features) - * [Why using hardware-based PWM is the best](#why-using-hardware-based-pwm-is-the-best) + * [Why using ISR-based PWM is better](#why-using-isr-based-pwm-is-better) * [Currently supported Boards](#currently-supported-boards) * [Important Notes about ISR](#important-notes-about-isr) +* [Changelog](changelog.md) * [Prerequisites](#prerequisites) * [Installation](#installation) * [Use Arduino Library Manager](#use-arduino-library-manager) * [Manual Install](#manual-install) * [VS Code & PlatformIO](#vs-code--platformio) +* [HOWTO Fix `Multiple Definitions` Linker Error](#howto-fix-multiple-definitions-linker-error) * [More useful Information about STM32 Timers](#more-useful-information-about-stm32-timers) * [Available Timers for STM32](#available-timers-for-STM32) * [Usage](#usage) * [1. Init Hardware Timer](#1-init-hardware-timer) * [2. Set PWM Frequency, dutycycle, attach irqCallbackStartFunc and irqCallbackStopFunc functions](#2-Set-PWM-Frequency-dutycycle-attach-irqCallbackStartFunc-and-irqCallbackStopFunc-functions) * [Examples](#examples) - * [ 1. PWM_Multi](examples/PWM_Multi) - * [ 2. PWM_Multi_Args](examples/PWM_Multi_Args) - * [ 3. PWMs_Array_Complex](examples/PWMs_Array_Complex) -* [Example PWMs_Array_Complex](#example-PWMs_Array_Complex) + * [ 1. ISR_16_PWMs_Array](examples/ISR_16_PWMs_Array) + * [ 2. ISR_16_PWMs_Array_Complex](examples/ISR_16_PWMs_Array_Complex) + * [ 3. ISR_16_PWMs_Array_Simple](examples/ISR_16_PWMs_Array_Simple) + * [ 4. ISR_Changing_PWM](examples/ISR_Changing_PWM) + * [ 5. ISR_Modify_PWM](examples/ISR_Modify_PWM) + * [ 6. multiFileProject](examples/multiFileProject) **New** +* [Example ISR_16_PWMs_Array_Complex](#Example-ISR_16_PWMs_Array_Complex) * [Debug Terminal Output Samples](#debug-terminal-output-samples) - * [1. PWMs_Array_Complex on NUCLEO_F767ZI](#1-PWMs_Array_Complex-on-NUCLEO_F767ZI) - * [2. PWMs_Array_Complex on NUCLEO_H743ZI2](#2-PWMs_Array_Complex-on-NUCLEO_H743ZI2) - * [3. PWMs_Array_Complex on NUCLEO_L552ZE_Q](#3-PWMs_Array_Complex-on-NUCLEO_L552ZE_Q) - * [4. PWMs_Array_Complex on BLUEPILL_F103CB](#4-PWMs_Array_Complex-on-BLUEPILL_F103CB) + * [1. ISR_16_PWMs_Array_Complex on NUCLEO_H743ZI2](#1-ISR_16_PWMs_Array_Complex-on-NUCLEO_H743ZI2) + * [2. ISR_16_PWMs_Array_Complex on NUCLEO_F767ZI](#2-ISR_16_PWMs_Array_Complex-on-NUCLEO_F767ZI) + * [3. ISR_16_PWMs_Array_Complex on NUCLEO_L552ZE_Q](#3-ISR_16_PWMs_Array_Complex-on-NUCLEO_L552ZE_Q) + * [4. ISR_16_PWMs_Array_Complex on BLUEPILL_F103CB](#4-ISR_16_PWMs_Array_Complex-on-BLUEPILL_F103CB) + * [5. ISR_Modify_PWM on NUCLEO_F767ZI](#5-ISR_Modify_PWM-on-NUCLEO_F767ZI) + * [6. ISR_Changing_PWM on NUCLEO_F767ZI](#6-ISR_Changing_PWM-on-NUCLEO_F767ZI) * [Debug](#debug) * [Troubleshooting](#troubleshooting) * [Issues](#issues) @@ -52,44 +60,68 @@ --- --- -### Why do we need this [STM32_PWM library](https://github.com/khoih-prog/STM32_PWM) +### Important Change from v1.2.0 + +Please have a look at [HOWTO Fix `Multiple Definitions` Linker Error](#howto-fix-multiple-definitions-linker-error) + +As more complex calculation and check **inside ISR** are introduced from v1.2.0, there is possibly some crash depending on use-case. + +You can modify to use larger `HW_TIMER_INTERVAL_US`, (from current 20uS), according to your board and use-case if crash happens. + + +``` +// Current 20uS +#define HW_TIMER_INTERVAL_US 20L +``` + + +--- +--- + +### Why do we need this [STM32_Slow_PWM library](https://github.com/khoih-prog/STM32_Slow_PWM) ### Features -This **wrapper library** enables you to use Hardware-based PWM on STM32F/L/H/G/WB/MP1 boards to create and output PWM to pins. +This library enables you to use Hardware Timers on **STM32F/L/H/G/WB/MP1 boards** such as NUCLEO_H743ZI2, NUCLEO_L552ZE_Q, NUCLEO_F767ZI, BLUEPILL_F103CB, etc., to create and output PWM to pins. Because this library doesn't use the powerful hardware-controlled PWM with limitations, the maximum PWM frequency is currently limited at **1000Hz**, which is suitable for many real-life applications. Now you can also modify PWM settings on-the-fly. --- -The most important feature is they're purely hardware-based PWM channels. Therefore, their executions are **very precise and not blocked by bad-behaving functions or tasks**. This important feature is absolutely necessary for mission-critical tasks. +This library enables you to use Interrupt from Hardware Timers on STM32F/L/H/G/WB/MP1 boards to create and output PWM to pins. It now supports 16 ISR-based synchronized PWM channels, while consuming only 1 Hardware Timer. PWM interval can be very long (uint32_t millisecs). The most important feature is they're ISR-based PWM channels. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware PWM channels, using interrupt, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software PWM using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy. + +As **Hardware Timers are rare, and very precious assets** of any board, this library now enables you to use up to **16 ISR-based synchronized PWM channels, while consuming only 1 Hardware Timer**. Timers' interval is very long (**ulong millisecs**). -These hardware PWM channels still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other ISR-based or software-based PWM using millis() or micros(). That's necessary if you need to measure some data requiring very high frequency and much better accuracy. +Now with these new **16 ISR-based PWM-channels**, the maximum interval is **practically unlimited** (limited only by unsigned long miliseconds) while **the accuracy is nearly perfect** compared to software timers. -The [**PWMs_Array_Complex**](examples/PWMs_Array_Complex) example will demonstrate the nearly perfect accuracy, compared to software timers, by printing the actual period / duty-cycle in `microsecs` of each of PWM-channels. +The most important feature is they're ISR-based PWM channels. Therefore, their executions are **not blocked by bad-behaving functions / tasks**. This important feature is absolutely necessary for mission-critical tasks. -The [**PWM_Multi_Args**](examples/PWM/PWM_Multi_Args) will demonstrate the usage of multichannel PWM using multiple Hardware Timers. The 4 independent Hardware Timers are used **to control 4 different PWM outputs**, with totally independent frequencies and dutycycles. +The [**PWMs_Array_Complex**](examples/PWMs_Array_Complex) example will demonstrate the nearly perfect accuracy, compared to software PWM, by printing the actual period / duty-cycle in `microsecs` of each of PWM-channels. -Being hardware-based PWM, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet or Blynk services. +Being ISR-based PWM, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet or Blynk services. You can also have many `(up to 16)` timers to use. This non-being-blocked important feature is absolutely necessary for mission-critical tasks. -You'll see `software-based` SimpleTimer is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking task in loop(), using delay() function as an example. The elapsed time then is very unaccurate +You'll see `software-based` SimpleTimer is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking task +in loop(), using delay() function as an example. The elapsed time then is very unaccurate --- -#### Why using hardware-based PWM is the best +#### Why using ISR-based PWM is better -Imagine you have a system with a **mission-critical** function, controlling a self-driving machine or robot or doing something much more important. You normally use a software timer to poll, or even place the function in loop(). But what if another function is **blocking** the loop() or setup(). +Imagine you have a system with a **mission-critical** function, measuring water level and control the sump pump or doing something much more important. You normally use a software timer to poll, or even place the function in loop(). But what if another function is **blocking** the loop() or setup(). So your function **might not be executed, and the result would be disastrous.** You'd prefer to have your function called, no matter what happening with other functions (busy loop, bug, etc.). -The correct choice is to use hardware-based PWM to control the PWM motors. +The correct choice is to use a Hardware Timer with **Interrupt** to call your function. + +These hardware timers, using interrupt, still work even if other functions are blocking. Moreover, they are much more **precise** (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy. -These hardware-based PWM channels, still work even if other functions are blocking. Moreover, they are much more **precise** (certainly depending on clock frequency accuracy) than other ISR-based or software-based PWM using millis() or micros(). That's necessary if you need to measure some data requiring very high accuracy. +Functions using normal software timers, relying on loop() and calling millis(), won't work if the loop() or setup() is blocked by certain operation. For example, certain function is blocking while it's connecting to WiFi or some services. -Functions using normal software-based PWM, relying on loop() and calling millis(), won't work if the loop() or setup() is blocked by certain operation. For example, certain function is blocking while it's connecting to WiFi or some services. +The catch is **your function is now part of an ISR (Interrupt Service Routine), and must be lean / mean, and follow certain rules.** More to read on: +[**HOWTO Attach Interrupt**](https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/) --- @@ -122,28 +154,52 @@ Functions using normal software-based PWM, relying on loop() and calling millis( ### Use Arduino Library Manager -The best and easiest way is to use `Arduino Library Manager`. Search for [**STM32_PWM**](https://github.com/khoih-prog/STM32_PWM), then select / install the latest version. -You can also use this link [![arduino-library-badge](https://www.ardu-badge.com/badge/STM32_PWM.svg?)](https://www.ardu-badge.com/STM32_PWM) for more detailed instructions. +The best and easiest way is to use `Arduino Library Manager`. Search for [**STM32_Slow_PWM**](https://github.com/khoih-prog/STM32_Slow_PWM), then select / install the latest version. +You can also use this link [![arduino-library-badge](https://www.ardu-badge.com/badge/STM32_Slow_PWM.svg?)](https://www.ardu-badge.com/STM32_Slow_PWM) for more detailed instructions. ### Manual Install Another way to install is to: -1. Navigate to [**STM32_PWM**](https://github.com/khoih-prog/STM32_PWM) page. -2. Download the latest release `STM32_PWM-master.zip`. -3. Extract the zip file to `STM32_PWM-master` directory -4. Copy whole `STM32_PWM-master` folder to Arduino libraries' directory such as `~/Arduino/libraries/`. +1. Navigate to [**STM32_Slow_PWM**](https://github.com/khoih-prog/STM32_Slow_PWM) page. +2. Download the latest release `STM32_Slow_PWM-master.zip`. +3. Extract the zip file to `STM32_Slow_PWM-master` directory +4. Copy whole `STM32_Slow_PWM-master` folder to Arduino libraries' directory such as `~/Arduino/libraries/`. ### VS Code & PlatformIO 1. Install [VS Code](https://code.visualstudio.com/) 2. Install [PlatformIO](https://platformio.org/platformio-ide) -3. Install [**STM32_PWM** library](https://platformio.org/lib/show/12902/STM32_PWM) by using [Library Manager](https://platformio.org/lib/show/12902/STM32_PWM/installation). Search for **STM32_PWM** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) +3. Install [**STM32_Slow_PWM** library](https://registry.platformio.org/libraries/khoih-prog/STM32_Slow_PWM) by using [Library Manager](https://registry.platformio.org/libraries/khoih-prog/STM32_Slow_PWM/installation). Search for **STM32_Slow_PWM** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) 4. Use included [platformio.ini](platformio/platformio.ini) file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at [Project Configuration File](https://docs.platformio.org/page/projectconf.html) --- --- +### HOWTO Fix `Multiple Definitions` Linker Error + +The current library implementation, using `xyz-Impl.h` instead of standard `xyz.cpp`, possibly creates certain `Multiple Definitions` Linker error in certain use cases. + +You can include this `.hpp` file + +``` +// Can be included as many times as necessary, without `Multiple Definitions` Linker Error +#include "STM32_Slow_PWM.hpp" //https://github.com/khoih-prog/STM32_Slow_PWM +``` + +in many files. But be sure to use the following `.h` file **in just 1 `.h`, `.cpp` or `.ino` file**, which must **not be included in any other file**, to avoid `Multiple Definitions` Linker Error + +``` +// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error +#include "STM32_Slow_PWM.h" //https://github.com/khoih-prog/STM32_Slow_PWM +``` + +Check the new [**multiFileProject** example](examples/multiFileProject) for a `HOWTO` demo. + +Have a look at the discussion in [Different behaviour using the src_cpp or src_h lib #80](https://github.com/khoih-prog/ESPAsync_WiFiManager/discussions/80) + +--- +--- ## More useful Information about STM32 Timers @@ -151,57 +207,64 @@ The Timers of STM32s are numerous, yet very sophisticated and powerful. In general, across the STM32 microcontrollers families, the timer peripherals that have the same name also have the same features set, but there are a few exceptions. +For example, the **TIM1** timer peripheral is shared across the STM32F1 Series, STM32F2 Series and STM32F4 Series, but for the specific case of STM32F30x microcontrollers family, the TIM1 timer peripheral features a bit richer features set than the TIM1 present in the other families. + The general purpose timers embedded by the STM32 microcontrollers share the same backbone structure; they differ only on the level of features embedded by a given timer peripheral. The level of features integration for a given timer peripheral is decided based on the applications field that it targets. The timer peripherals can be classified as: - • Advanced-configuration timers like TIM1 and TIM8 among others. • General-purpose configuration timers like TIM2 and TIM3 among others • Lite-configuration timers like TIM9, TIM10, TIM12 and TIM16 among others • Basic-configuration timers like TIM6 and TIM7 among others. +For example, **STM32F103C8T6** has one advance timer, while **STM32F103VET6** has two advanced timers. **Nucleo-144 STM32F767ZI boards have 14 Timers, TIM1-TIM14**. -More information can be found at [**Embedded-Lab STM32 TIMERS**](http://embedded-lab.com/blog/stm32-timers/) -To be sure which Timer is available for the board you're using, check the Core Package's related files. For example, for **STM32 using STM32H747XI**, check this file: +

+ +

+ -1. `~/.arduino15/packages/STM32/hardware/stm32/2.0.0/system/Drivers/CMSIS/Device/ST/STM32H7xx/Include/stm32h7xx.h` +More information can be found at [**Embedded-Lab STM32 TIMERS**](http://embedded-lab.com/blog/stm32-timers/) +To be sure which Timer is available for the board you're using, check the Core Package's related files. For example, for **Nucleo-144 STM32F767ZI**, check these files: +1. `~/.arduino15/packages/STM32/hardware/stm32/1.9.0/system/Drivers/CMSIS/Device/ST/STM32F7xx/Include/stm32f7xx.h` +2. `~/.arduino15/packages/STM32/hardware/stm32/1.9.0/system/Drivers/CMSIS/Device/ST/STM32F7xx/Include/stm32f767xx.h` The information will be as follows: ``` typedef struct { - __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ - __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ - __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ - __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ - __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ - __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ - __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ - __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ - __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ - __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ - __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ - __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ - __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ - __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ - __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ - __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ - __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ - __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ - __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ - __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */ - uint32_t RESERVED1; /*!< Reserved, 0x50 */ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */ + __IO uint32_t OR; /*!< TIM option register, Address offset: 0x50 */ __IO uint32_t CCMR3; /*!< TIM capture/compare mode register 3, Address offset: 0x54 */ - __IO uint32_t CCR5; /*!< TIM capture/compare register5, Address offset: 0x58 */ - __IO uint32_t CCR6; /*!< TIM capture/compare register6, Address offset: 0x5C */ - __IO uint32_t AF1; /*!< TIM alternate function option register 1, Address offset: 0x60 */ - __IO uint32_t AF2; /*!< TIM alternate function option register 2, Address offset: 0x64 */ - __IO uint32_t TISEL; /*!< TIM Input Selection register, Address offset: 0x68 */ + __IO uint32_t CCR5; /*!< TIM capture/compare mode register5, Address offset: 0x58 */ + __IO uint32_t CCR6; /*!< TIM capture/compare mode register6, Address offset: 0x5C */ + __IO uint32_t AF1; /*!< TIM Alternate function option register 1, Address offset: 0x60 */ + __IO uint32_t AF2; /*!< TIM Alternate function option register 2, Address offset: 0x64 */ + } TIM_TypeDef; ``` @@ -213,71 +276,57 @@ and #define APB1PERIPH_BASE PERIPH_BASE /*!< APB1 peripherals */ -/*!< D2_APB1PERIPH peripherals */ -#define TIM2_BASE (D2_APB1PERIPH_BASE + 0x0000UL) -#define TIM3_BASE (D2_APB1PERIPH_BASE + 0x0400UL) -#define TIM4_BASE (D2_APB1PERIPH_BASE + 0x0800UL) -#define TIM5_BASE (D2_APB1PERIPH_BASE + 0x0C00UL) -#define TIM6_BASE (D2_APB1PERIPH_BASE + 0x1000UL) -#define TIM7_BASE (D2_APB1PERIPH_BASE + 0x1400UL) -#define TIM12_BASE (D2_APB1PERIPH_BASE + 0x1800UL) -#define TIM13_BASE (D2_APB1PERIPH_BASE + 0x1C00UL) -#define TIM14_BASE (D2_APB1PERIPH_BASE + 0x2000UL) +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000UL) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400UL) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800UL) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00UL) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000UL) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400UL) +#define TIM12_BASE (APB1PERIPH_BASE + 0x1800UL) +#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00UL) +#define TIM14_BASE (APB1PERIPH_BASE + 0x2000UL) /*!< APB2 peripherals */ -#define TIM1_BASE (D2_APB2PERIPH_BASE + 0x0000UL) -#define TIM8_BASE (D2_APB2PERIPH_BASE + 0x0400UL) -... +#define TIM1_BASE (APB2PERIPH_BASE + 0x0000UL) +#define TIM8_BASE (APB2PERIPH_BASE + 0x0400UL) #define TIM9_BASE (APB2PERIPH_BASE + 0x4000UL) #define TIM10_BASE (APB2PERIPH_BASE + 0x4400UL) #define TIM11_BASE (APB2PERIPH_BASE + 0x4800UL) + ... -#define TI15_BASE (D2_APB2PERIPH_BASE + 0x4000UL) -#define TIM16_BASE (D2_APB2PERIPH_BASE + 0x4400UL) -#define TIM17_BASE (D2_APB2PERIPH_BASE + 0x4800UL) -... -#define HRTIM1_BASE (D2_APB2PERIPH_BASE + 0x7400UL) -#define HRTIM1_TIMA_BASE (HRTIM1_BASE + 0x00000080UL) -#define HRTIM1_TIMB_BASE (HRTIM1_BASE + 0x00000100UL) -#define HRTIM1_TIMC_BASE (HRTIM1_BASE + 0x00000180UL) -#define HRTIM1_TIMD_BASE (HRTIM1_BASE + 0x00000200UL) -#define HRTIM1_TIME_BASE (HRTIM1_BASE + 0x00000280UL) -#define HRTIM1_COMMON_BASE (HRTIM1_BASE + 0x00000380UL) -... + #define TIM2 ((TIM_TypeDef *) TIM2_BASE) #define TIM3 ((TIM_TypeDef *) TIM3_BASE) #define TIM4 ((TIM_TypeDef *) TIM4_BASE) #define TIM5 ((TIM_TypeDef *) TIM5_BASE) #define TIM6 ((TIM_TypeDef *) TIM6_BASE) #define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define TIM12 ((TIM_TypeDef *) TIM12_BASE) #define TIM13 ((TIM_TypeDef *) TIM13_BASE) #define TIM14 ((TIM_TypeDef *) TIM14_BASE) ... #define TIM1 ((TIM_TypeDef *) TIM1_BASE) #define TIM8 ((TIM_TypeDef *) TIM8_BASE) ... -#define TIM12 ((TIM_TypeDef *) TIM12_BASE) -#define TIM15 ((TIM_TypeDef *) TIM15_BASE) -#define TIM16 ((TIM_TypeDef *) TIM16_BASE) -#define TIM17 ((TIM_TypeDef *) TIM17_BASE) -... -#define HRTIM1 ((HRTIM_TypeDef *) HRTIM1_BASE) -#define HRTIM1_TIMA ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMA_BASE) -#define HRTIM1_TIMB ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMB_BASE) -#define HRTIM1_TIMC ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMC_BASE) -#define HRTIM1_TIMD ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMD_BASE) -#define HRTIM1_TIME ((HRTIM_Timerx_TypeDef *) HRTIM1_TIME_BASE) -#define HRTIM1_COMMON ((HRTIM_Common_TypeDef *) HRTIM1_COMMON_BASE) +#define TIM9 ((TIM_TypeDef *) TIM9_BASE) +#define TIM10 ((TIM_TypeDef *) TIM10_BASE) +#define TIM11 ((TIM_TypeDef *) TIM11_BASE) + ``` + --- ## Available Timers for STM32 -This is the temporary list for STM32 Timers which can be used. The available Timers certainly depends on they are being used for other purpose (core, application, libraries, etc.) or not. You have to exhausively test yourself to be sure. +Because STM32 boards and Timers are numerous, changing and so complex, there can never be a complete and correct list of available Timers to use. + +This is the temporary list for **STM32F/L/H/G/WB/MP1** Timers which can possibly be used. The available Timers certainly depends on whether they are being used for other purpose (core, application, libraries, etc.) or not. You have to `exhausively` test, research, verify by yourself to be sure. #### 1. OK to use +If exists, otherwise, compiler error will happen + **TIM1, TIM4, TIM7, TIM8, TIM12, TIM13, TIM14, TIM15, TIM16, TIM17** #### 2. Not exist @@ -303,15 +352,31 @@ Before using any Timer for a PWM channel, you have to make sure the Timer has no #### 1. Init Hardware Timer ``` -// Automatically retrieve TIM instance and channel associated to pin -// This is used to be compatible with all STM32 series automatically. -TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(pinNameToUse, PinMap_PWM); +// Depending on the board, you can select STM32H7 Hardware Timer from TIM1-TIM22 +// If you select a Timer not correctly, you'll get a message from compiler +// 'TIMxx' was not declared in this scope; did you mean 'TIMyy'? + +// STM32F/L/H/G/WB/MP1 OK : TIM1, TIM4, TIM7, TIM8, TIM12, TIM13, TIM14, TIM15, TIM16, TIM17 +// STM32F/L/H/G/WB/MP1 Not OK : TIM2, TIM3, TIM5, TIM6, TIM18, TIM19, TIM20, TIM21, TIM22 +// STM32F/L/H/G/WB/MP1 No timer : TIM9, TIM10, TIM11. Only for STM32F2, STM32F4 and STM32L1 +// STM32F/L/H/G/WB/MP1 No timer : TIM18, TIM19, TIM20, TIM21, TIM22 + +// Init timer TIM1 +STM32Timer ITimer(TIM1); + +// Init STM32_Slow_PWM +STM32_Slow_PWM ISR_PWM; ``` #### 2. Set PWM Frequency, dutycycle, attach irqCallbackStartFunc and irqCallbackStopFunc functions ``` -void PeriodCallback() +void irqCallbackStartFunc() +{ + +} + +void irqCallbackStopFunc() { } @@ -320,7 +385,10 @@ void setup() { .... - MyTim->setPWM(channel, pins, freq, dutyCycle, PeriodCallback); + // You can use this with PWM_Freq in Hz + ISR_PWM.setPWM(PWM_Pin, PWM_Freq, PWM_DutyCycle, irqCallbackStartFunc, irqCallbackStopFunc); + + .... } ``` @@ -329,609 +397,337 @@ void setup() ### Examples: - 1. [PWM_Multi](examples/PWM_Multi) - 2. [PWM_Multi_Args](examples/PWM_Multi_Args) - 3. [PWMs_Array_Complex](examples/PWMs_Array_Complex) - + 1. [ISR_16_PWMs_Array](examples/ISR_16_PWMs_Array) + 2. [ISR_16_PWMs_Array_Complex](examples/ISR_16_PWMs_Array_Complex) + 3. [ISR_16_PWMs_Array_Simple](examples/ISR_16_PWMs_Array_Simple) + 4. [ISR_Changing_PWM](examples/ISR_Changing_PWM) + 5. [ISR_Modify_PWM](examples/ISR_Modify_PWM) + 6. [**multiFileProject**](examples/multiFileProject) **New** --- --- -### Example [PWMs_Array_Complex](examples/PWMs_Array_Complex) - -``` -#if !( defined(STM32F0) || defined(STM32F1) || defined(STM32F2) || defined(STM32F3) ||defined(STM32F4) || defined(STM32F7) || \ - defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32H7) ||defined(STM32G0) || defined(STM32G4) || \ - defined(STM32WB) || defined(STM32MP1) || defined(STM32L5)) - #error This code is designed to run on STM32F/L/H/G/WB/MP1 platform! Please check your Tools->Board setting. -#endif - -// These define's must be placed at the beginning before #include "ESP32_PWM.h" -// _PWM_LOGLEVEL_ from 0 to 4 -// Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. -#define _PWM_LOGLEVEL_ 4 - -#define USING_MICROS_RESOLUTION true //false - -#include "STM32_PWM.h" - -#define LED_ON LOW -#define LED_OFF HIGH - -#ifndef LED_BUILTIN -#define LED_BUILTIN 13 -#endif - -#ifndef LED_BLUE -#define LED_BLUE 2 -#endif - -#ifndef LED_RED -#define LED_RED 3 -#endif - -#define boolean bool - -#include // https://github.com/jfturcot/SimpleTimer - -#define HW_TIMER_INTERVAL_US 10L - -volatile uint64_t startMicros = 0; - - -///////////////////////////////////////////////// - -// Change the pin according to your STM32 board. There is no single definition for all boards. -#define pin0 PA0 -#define pin1 D1 -#define pin2 D2 -#define pin3 D3 -#define pin4 D4 -#define pin5 D5 -#define pin6 D6 -#define pin7 D7 - -#define pin8 D8 -#define pin9 D9 -#define pin10 D10 -#define pin11 D11 -#define pin12 D12 -#define pin13 D13 -#define pin14 D14 -#define pin15 D15 -#define pin16 D16 - -////////////////////////////////////////////////////// - -// For F103CB -// - -// Change the pin according to your STM32 board. There is no single definition for all boards. - -#if ( defined(STM32F1) && ( defined(ARDUINO_BLUEPILL_F103CB) || defined(ARDUINO_BLUEPILL_F103C8) ) ) - - #warning Using BLUEPILL_F103CB / BLUEPILL_F103C8 pins - - // For F103CB => pins[] = pin0, pin4, pin10 ============>> TimerIndex = 1, 2, 0 - uint32_t pins[] = { pin0, pin4, pin10 }; - -#elif ( defined(STM32F7) && defined(ARDUINO_NUCLEO_F767ZI) ) - - #warning Using NUCLEO_F767ZI pins - - // For F767ZI => pins[] = pin0, pin3, pin9/10 ============>> TimerIndex = 1, 0, 3 - uint32_t pins[] = { pin0, pin3, pin9 }; - -#elif ( defined(STM32L5) && defined(ARDUINO_NUCLEO_L552ZE_Q) ) - - #warning Using NUCLEO_L552ZE_Q pins - - // For NUCLEO_L552ZE_Q => pins[] = pin0, pin3, pin9/10 ============>> TimerIndex = 1, 0, 3 - uint32_t pins[] = { pin0, pin3, pin9 }; - -#elif ( defined(STM32H7) && defined(ARDUINO_NUCLEO_H743ZI2) ) - - #warning Using NUCLEO_H743ZI2 pins - - // For NUCLEO_L552ZE_Q => pins[] = pin0, pin3, pin9/10 ============>> TimerIndex = 1, 0, 3 - uint32_t pins[] = { pin0, pin3, pin9 }; +### Example [ISR_16_PWMs_Array_Complex](examples/ISR_16_PWMs_Array_Complex) -#else +https://github.com/khoih-prog/STM32_PWM/blob/4e4a7fe81e69b972ebab9e30b472887adfcd4250/examples/ISR_16_PWMs_Array_Complex/ISR_16_PWMs_Array_Complex.ino#L16-L592 - // For ??? => pins[] = pin0, pin3, pin9/10 ============>> TimerIndex = 1, 0, 3 - uint32_t pins[] = { pin0, pin3, pin9 }; - -#endif - -////////////////////////////////////////////////////// - -#define NUM_OF_PINS ( sizeof(pins) / sizeof(uint32_t) ) - -#define NUMBER_ISR_PWMS NUM_OF_PINS - -TIM_TypeDef *TimerInstance[] = { TIM1, TIM2, TIM3 }; - -volatile uint32_t callbackTime[] = { 0, 0, 0 }; - -////////////////////////////////////////////////////// - -#define USE_COMPLEX_STRUCT true - -////////////////////////////////////////////////////// - -#if USE_COMPLEX_STRUCT - -typedef struct -{ - //uint32_t PWM_Pin; - - callback_function_t irqCallbackStartFunc; - callback_function_t irqCallbackStopFunc; - - uint32_t PWM_Freq; - uint32_t PWM_DutyCycle; - uint64_t deltaMicrosStart; - uint64_t previousMicrosStart; - - uint64_t deltaMicrosStop; - uint64_t previousMicrosStop; - -} ISR_PWM_Data; - -// Avoid doing something fancy in ISR, for example Serial.print() -// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment -// Or you can get this run-time error / crash - -void doingSomethingStart(int index); - -void doingSomethingStop(int index); - -#else // #if USE_COMPLEX_STRUCT - -volatile uint64_t deltaMicrosStart [] = { 0, 0, 0 }; -volatile uint64_t previousMicrosStart [] = { 0, 0, 0 }; - -volatile uint64_t deltaMicrosStop [] = { 0, 0, 0 }; -volatile uint64_t previousMicrosStop [] = { 0, 0, 0 }; - -// You can assign any interval for any timer here, in Hz -uint32_t PWM_Freq[] = -{ - 10, 20, 500 -}; - -// You can assign any interval for any timer here, in milliseconds -uint32_t PWM_DutyCycle[] = -{ - 20, 30, 50 -}; - -void doingSomethingStart(int index) -{ - uint64_t currentMicros = micros(); - deltaMicrosStart[index] = currentMicros - previousMicrosStart[index]; - previousMicrosStart[index] = currentMicros; -} - -void doingSomethingStop(int index) -{ - uint64_t currentMicros = micros(); - - // Count from start to stop PWM pulse - deltaMicrosStop[index] = currentMicros - previousMicrosStart[index]; - - previousMicrosStop[index] = currentMicros; -} - -#endif // #if USE_COMPLEX_STRUCT - -//////////////////////////////////// -// Shared -//////////////////////////////////// - -void doingSomethingStart0() -{ - static bool ledON = LED_OFF; - - doingSomethingStart(0); - - callbackTime[0]++; - - digitalWrite(LED_BUILTIN, ledON); - - ledON = !ledON; -} - -void doingSomethingStart1() -{ - static bool ledON = LED_OFF; - - doingSomethingStart(1); - - callbackTime[1]++; - - digitalWrite(LED_BLUE, ledON); - - ledON = !ledON; -} - -void doingSomethingStart2() -{ - static bool ledON = LED_OFF; - - doingSomethingStart(2); - - digitalWrite(LED_RED, ledON); - - callbackTime[2]++; - - ledON = !ledON; -} - - -////////////////////////////////////////////////////// - -void doingSomethingStop0() -{ - doingSomethingStop(0); -} - -void doingSomethingStop1() -{ - doingSomethingStop(1); -} - -void doingSomethingStop2() -{ - doingSomethingStop(2); -} - - -////////////////////////////////////////////////////// - -#if USE_COMPLEX_STRUCT - -ISR_PWM_Data curISR_PWM_Data[] = -{ - // irqCallbackStartFunc, irqCallbackStopFunc, PWM_Freq, deltaMicrosStart, previousMicrosStart, deltaMicrosStop, previousMicrosStop - { doingSomethingStart0, doingSomethingStop0, 10, 20, 0, 0, 0, 0 }, - { doingSomethingStart1, doingSomethingStop1, 20, 30, 0, 0, 0, 0 }, - { doingSomethingStart2, doingSomethingStop2, 500, 50, 0, 0, 0, 0 }, -}; - -void doingSomethingStart(int index) -{ - uint64_t currentMicros = micros(); - - curISR_PWM_Data[index].deltaMicrosStart = currentMicros - curISR_PWM_Data[index].previousMicrosStart; - curISR_PWM_Data[index].previousMicrosStart = currentMicros; -} - -void doingSomethingStop(int index) -{ - uint64_t currentMicros = micros(); - - // Count from start to stop PWM pulse - curISR_PWM_Data[index].deltaMicrosStop = currentMicros - curISR_PWM_Data[index].previousMicrosStart; - curISR_PWM_Data[index].previousMicrosStop = currentMicros; -} - -#else // #if USE_COMPLEX_STRUCT - -callback_function_t irqCallbackStartFunc[] = -{ - doingSomethingStart0, doingSomethingStart1, doingSomethingStart2 -}; - -callback_function_t irqCallbackStopFunc[] = -{ - doingSomethingStop0, doingSomethingStop1, doingSomethingStop2 -}; - -#endif // #if USE_COMPLEX_STRUCT - -////////////////////////////////////////////////////// - -#define SIMPLE_TIMER_MS 2000L - -// Init SimpleTimer -SimpleTimer simpleTimer; - -// Here is software Timer, you can do somewhat fancy stuffs without many issues. -// But always avoid -// 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead -// 2. Very long "do", "while", "for" loops without predetermined exit time. -void simpleTimerDoingSomething2s() -{ - static uint64_t previousMicrosStart = startMicros; - - uint64_t currMicros = micros(); - - Serial.print(F("SimpleTimer (ms): ")); Serial.print(SIMPLE_TIMER_MS); - Serial.print(F(", us : ")); Serial.print(currMicros); - Serial.print(F(", Dus : ")); Serial.println(currMicros - previousMicrosStart); - - for (uint16_t i = 0; i < NUMBER_ISR_PWMS; i++) - { -#if USE_COMPLEX_STRUCT - Serial.print(F("PWM Channel : ")); Serial.print(i); - Serial.print(F(", programmed Period (us): ")); - - Serial.print(1000000 / curISR_PWM_Data[i].PWM_Freq); - - Serial.print(F(", actual : ")); Serial.print(curISR_PWM_Data[i].deltaMicrosStart); - - Serial.print(F(", programmed DutyCycle : ")); - - Serial.print(curISR_PWM_Data[i].PWM_DutyCycle); - - Serial.print(F(", actual : ")); Serial.println((float) curISR_PWM_Data[i].deltaMicrosStop * 100.0f / curISR_PWM_Data[i].deltaMicrosStart); - -#else - - Serial.print(F("PWM Channel : ")); Serial.print(i); - - Serial.print(1000000 / PWM_Freq[i]); - - Serial.print(F(", programmed Period (us): ")); Serial.print(1000000 / PWM_Freq[i]); - Serial.print(F(", actual : ")); Serial.print(deltaMicrosStart[i]); - - Serial.print(F(", programmed DutyCycle : ")); - - Serial.print(PWM_DutyCycle[i]); - - Serial.print(F(", actual : ")); Serial.println( (float) deltaMicrosStop[i] * 100.0f / deltaMicrosStart[i]); -#endif - } - - previousMicrosStart = currMicros; -} - -////////////////////////////////////////////////////// - -void setup() -{ - pinMode(LED_BUILTIN, OUTPUT); - pinMode(LED_BLUE, OUTPUT); - pinMode(LED_RED, OUTPUT); - - digitalWrite(LED_BUILTIN, LED_OFF); - digitalWrite(LED_BLUE, LED_OFF); - digitalWrite(LED_RED, LED_OFF); - - for (uint8_t index = 0; index < NUM_OF_PINS; index++) - { - pinMode(pins[index], OUTPUT); - digitalWrite(pins[index], LOW); - } - - Serial.begin(115200); - while (!Serial); - - delay(2000); - - Serial.print(F("\nStarting PWMs_Array_Complex on ")); Serial.println(BOARD_NAME); - Serial.println(STM32_PWM_VERSION); - // Automatically retrieve TIM instance and channel associated to pin - // This is used to be compatible with all STM32 series automatically. - - startMicros = micros(); +--- +--- - for (uint8_t index = 0; index < NUM_OF_PINS; index++) - { - //TIM_TypeDef *Instance = TimerInstance[index]; +### Debug Terminal Output Samples - uint16_t Timer_Used[NUM_OF_PINS]; +### 1. ISR_16_PWMs_Array_Complex on NUCLEO_H743ZI2 - // Set unused TimerIndex = 0xFFFF - memset(Timer_Used, 0xFFFF, sizeof(Timer_Used)); +The following is the sample terminal output when running example [ISR_16_PWMs_Array_Complex](examples/ISR_16_PWMs_Array_Complex) on **NUCLEO_H743ZI2** to demonstrate how to use multiple PWM channels with complex callback functions, the accuracy of ISR Hardware PWM-channels, **especially when system is very busy**. The ISR PWM-channels is **running exactly according to corresponding programmed periods and duty-cycles** - // Using pin = PA0, PA1, etc. - PinName pinNameToUse = digitalPinToPinName(pins[index]); - // Automatically retrieve TIM instance and channel associated to pin - // This is used to be compatible with all STM32 series automatically. - TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(pinNameToUse, PinMap_PWM); +``` +Starting ISR_16_PWMs_Array_Complex on NUCLEO_H743ZI2 +STM32_SLOW_PWM v1.2.3 +[PWM] STM32TimerInterrupt: Timer Input Freq (Hz) = 240000000 +[PWM] Frequency = 1000000.00 , _count = 20 +Starting ITimer OK, micros() = 2015843 +Channel : 0 Period : 1000000 OnTime : 50000 Start_Time : 2019319 +Channel : 1 Period : 500000 OnTime : 50000 Start_Time : 2019319 +Channel : 2 Period : 333333 OnTime : 66666 Start_Time : 2019319 +Channel : 3 Period : 250000 OnTime : 75000 Start_Time : 2019319 +Channel : 4 Period : 200000 OnTime : 80000 Start_Time : 2019319 +Channel : 5 Period : 166666 OnTime : 74999 Start_Time : 2019319 +Channel : 6 Period : 142857 OnTime : 71428 Start_Time : 2019319 +Channel : 7 Period : 125000 OnTime : 68750 Start_Time : 2019319 +Channel : 8 Period : 111111 OnTime : 66666 Start_Time : 2019319 +Channel : 9 Period : 100000 OnTime : 65000 Start_Time : 2019319 +Channel : 10 Period : 66666 OnTime : 46666 Start_Time : 2019319 +Channel : 11 Period : 50000 OnTime : 37500 Start_Time : 2019319 +Channel : 12 Period : 40000 OnTime : 32000 Start_Time : 2019319 +Channel : 13 Period : 33333 OnTime : 28333 Start_Time : 2019319 +Channel : 14 Period : 25000 OnTime : 22500 Start_Time : 2019319 +Channel : 15 Period : 20000 OnTime : 19000 Start_Time : 2019319 +SimpleTimer (ms): 2000, us : 12111000, Dus : 10091682 +PWM Channel : 0, programmed Period (uS): 1000000, actual (uS) : 1000000, programmed DutyCycle : 5, actual : 5.00 +PWM Channel : 1, programmed Period (uS): 500000, actual (uS) : 500000, programmed DutyCycle : 10, actual : 10.00 +PWM Channel : 2, programmed Period (uS): 333333, actual (uS) : 333340, programmed DutyCycle : 20, actual : 20.00 +PWM Channel : 3, programmed Period (uS): 250000, actual (uS) : 250000, programmed DutyCycle : 30, actual : 30.00 +PWM Channel : 4, programmed Period (uS): 200000, actual (uS) : 200020, programmed DutyCycle : 40, actual : 40.00 +PWM Channel : 5, programmed Period (uS): 166666, actual (uS) : 166680, programmed DutyCycle : 45, actual : 45.00 +PWM Channel : 6, programmed Period (uS): 142857, actual (uS) : 142860, programmed DutyCycle : 50, actual : 49.99 +PWM Channel : 7, programmed Period (uS): 125000, actual (uS) : 125020, programmed DutyCycle : 55, actual : 54.98 +PWM Channel : 8, programmed Period (uS): 111111, actual (uS) : 111120, programmed DutyCycle : 60, actual : 59.99 +PWM Channel : 9, programmed Period (uS): 100000, actual (uS) : 100020, programmed DutyCycle : 65, actual : 64.99 +PWM Channel : 10, programmed Period (uS): 66666, actual (uS) : 66680, programmed DutyCycle : 70, actual : 69.98 +PWM Channel : 11, programmed Period (uS): 50000, actual (uS) : 50000, programmed DutyCycle : 75, actual : 75.00 +PWM Channel : 12, programmed Period (uS): 40000, actual (uS) : 40000, programmed DutyCycle : 80, actual : 80.00 +PWM Channel : 13, programmed Period (uS): 33333, actual (uS) : 33340, programmed DutyCycle : 85, actual : 84.94 +PWM Channel : 14, programmed Period (uS): 25000, actual (uS) : 25000, programmed DutyCycle : 90, actual : 90.00 +PWM Channel : 15, programmed Period (uS): 20000, actual (uS) : 20000, programmed DutyCycle : 95, actual : 95.00 +SimpleTimer (ms): 2000, us : 22266000, Dus : 10155000 +PWM Channel : 0, programmed Period (uS): 1000000, actual (uS) : 1000000, programmed DutyCycle : 5, actual : 5.00 +PWM Channel : 1, programmed Period (uS): 500000, actual (uS) : 500000, programmed DutyCycle : 10, actual : 10.00 +PWM Channel : 2, programmed Period (uS): 333333, actual (uS) : 333340, programmed DutyCycle : 20, actual : 20.00 +PWM Channel : 3, programmed Period (uS): 250000, actual (uS) : 250020, programmed DutyCycle : 30, actual : 30.00 +PWM Channel : 4, programmed Period (uS): 200000, actual (uS) : 200020, programmed DutyCycle : 40, actual : 40.00 +PWM Channel : 5, programmed Period (uS): 166666, actual (uS) : 166680, programmed DutyCycle : 45, actual : 44.98 +PWM Channel : 6, programmed Period (uS): 142857, actual (uS) : 142860, programmed DutyCycle : 50, actual : 49.99 +PWM Channel : 7, programmed Period (uS): 125000, actual (uS) : 125000, programmed DutyCycle : 55, actual : 54.99 +PWM Channel : 8, programmed Period (uS): 111111, actual (uS) : 111120, programmed DutyCycle : 60, actual : 59.99 +PWM Channel : 9, programmed Period (uS): 100000, actual (uS) : 100000, programmed DutyCycle : 65, actual : 65.00 +PWM Channel : 10, programmed Period (uS): 66666, actual (uS) : 66680, programmed DutyCycle : 70, actual : 69.98 +PWM Channel : 11, programmed Period (uS): 50000, actual (uS) : 50000, programmed DutyCycle : 75, actual : 75.00 +PWM Channel : 12, programmed Period (uS): 40000, actual (uS) : 40000, programmed DutyCycle : 80, actual : 80.00 +PWM Channel : 13, programmed Period (uS): 33333, actual (uS) : 33340, programmed DutyCycle : 85, actual : 84.94 +PWM Channel : 14, programmed Period (uS): 25000, actual (uS) : 25020, programmed DutyCycle : 90, actual : 89.93 +PWM Channel : 15, programmed Period (uS): 20000, actual (uS) : 20000, programmed DutyCycle : 95, actual : 94.91 +``` - if (Instance != nullptr) - { - uint8_t timerIndex = get_timer_index(Instance); - - // pin => 0, 1, etc - uint32_t channel = STM_PIN_CHANNEL(pinmap_function( pinNameToUse, PinMap_PWM)); - - Serial.print("Index = "); Serial.print(index); - Serial.print(", Instance = 0x"); Serial.print( (uint32_t) Instance, HEX); - Serial.print(", channel = "); Serial.print(channel); - Serial.print(", TimerIndex = "); Serial.print(get_timer_index(Instance)); - Serial.print(", PinName = "); Serial.println( pinNameToUse ); - - for ( uint8_t i = 0; i < index; i++) - { - if (timerIndex == Timer_Used[i]) - { - Serial.print("ERROR => Already used TimerIndex for index = "); Serial.println(index); - Serial.print(", TimerIndex = "); Serial.println(timerIndex); - //break; - while (true) - delay(100); - } - } - - // Update Timer_Used with current timerIndex - Timer_Used[index] = timerIndex; - - HardwareTimer *MyTim = new HardwareTimer(Instance); +--- - #if USE_COMPLEX_STRUCT - - //MyTim->setPWM(channel, curISR_PWM_Data[index].PWM_Pin, curISR_PWM_Data[index].PWM_Freq, curISR_PWM_Data[index].PWM_DutyCycle, - // curISR_PWM_Data[index].irqCallbackStartFunc, curISR_PWM_Data[index].irqCallbackStopFunc); - MyTim->setPWM(channel, pins[index], curISR_PWM_Data[index].PWM_Freq, curISR_PWM_Data[index].PWM_DutyCycle, - curISR_PWM_Data[index].irqCallbackStartFunc, curISR_PWM_Data[index].irqCallbackStopFunc); - - #else - - //MyTim->setPWM(channel, PWM_Pin[index], PWM_Freq[index], PWM_DutyCycle[index], irqCallbackStartFunc[index], irqCallbackStopFunc[index]); - MyTim->setPWM(channel, pins[index], PWM_Freq[index], PWM_DutyCycle[index], irqCallbackStartFunc[index], irqCallbackStopFunc[index]); - - #endif - } - else - { - Serial.print("ERROR => Wrong pin, You have to select another one. Skip NULL Instance for index = "); Serial.println(index); - } - } - - // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. - simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); -} +### 2. ISR_16_PWMs_Array_Complex on NUCLEO_F767ZI -////////////////////////////////////////////////////// +The following is the sample terminal output when running example [ISR_16_PWMs_Array_Complex](examples/ISR_16_PWMs_Array_Complex) on **NUCLEO_F767ZI** to demonstrate how to use multiple PWM channels with complex callback functions, the accuracy of ISR Hardware PWM-channels, **especially when system is very busy**. The ISR PWM-channels is **running exactly according to corresponding programmed periods and duty-cycles** -#define BLOCKING_TIME_MS 10000L -void loop() -{ - // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer - // You see the time elapse of ISR_PWM still accurate, whereas very unaccurate for Software Timer - // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) - // While that of ISR_PWM is still prefect. - delay(BLOCKING_TIME_MS); - - // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary - // You don't need to and never call ISR_PWM.run() here in the loop(). It's already handled by ISR timer. - simpleTimer.run(); -} ``` ---- ---- +Starting ISR_16_PWMs_Array_Complex on NUCLEO_F767ZI +STM32_SLOW_PWM v1.2.3 +[PWM] STM32TimerInterrupt: Timer Input Freq (Hz) = 216000000 , Timer Clock Frequency = 1000000.00 +[PWM] Timer Frequency = 50000.00 , _count = 20 +Starting ITimer OK, micros() = 2016555 +Channel : 0 Period : 1000000 OnTime : 50000 Start_Time : 2022112 +Channel : 1 Period : 500000 OnTime : 50000 Start_Time : 2027666 +Channel : 2 Period : 333333 OnTime : 66666 Start_Time : 2033231 +Channel : 3 Period : 250000 OnTime : 75000 Start_Time : 2038795 +Channel : 4 Period : 200000 OnTime : 80000 Start_Time : 2044365 +Channel : 5 Period : 166666 OnTime : 74999 Start_Time : 2049930 +Channel : 6 Period : 142857 OnTime : 71428 Start_Time : 2055495 +Channel : 7 Period : 125000 OnTime : 68750 Start_Time : 2061063 +Channel : 8 Period : 111111 OnTime : 66666 Start_Time : 2066628 +Channel : 9 Period : 100000 OnTime : 65000 Start_Time : 2072361 +Channel : 10 Period : 66666 OnTime : 46666 Start_Time : 2083313 +Channel : 11 Period : 50000 OnTime : 37500 Start_Time : 2088886 +Channel : 12 Period : 40000 OnTime : 32000 Start_Time : 2094451 +Channel : 13 Period : 33333 OnTime : 28333 Start_Time : 2100013 +Channel : 14 Period : 25000 OnTime : 22500 Start_Time : 2105584 +Channel : 15 Period : 20000 OnTime : 19000 Start_Time : 2111150 +SimpleTimer (ms): 2000, us : 12116002, Dus : 10093908 +PWM Channel : 0, programmed Period (uS): 1000000.00, actual (uS) : 1000000, programmed DutyCycle : 5.00, actual : 5.00 +PWM Channel : 1, programmed Period (uS): 500000.00, actual (uS) : 500000, programmed DutyCycle : 10.00, actual : 10.00 +PWM Channel : 2, programmed Period (uS): 333333.34, actual (uS) : 333340, programmed DutyCycle : 20.00, actual : 20.00 +PWM Channel : 3, programmed Period (uS): 250000.00, actual (uS) : 250000, programmed DutyCycle : 30.00, actual : 30.00 +PWM Channel : 4, programmed Period (uS): 200000.00, actual (uS) : 200000, programmed DutyCycle : 40.00, actual : 40.00 +PWM Channel : 5, programmed Period (uS): 166666.67, actual (uS) : 166680, programmed DutyCycle : 45.00, actual : 44.98 +PWM Channel : 6, programmed Period (uS): 142857.14, actual (uS) : 142860, programmed DutyCycle : 50.00, actual : 49.99 +PWM Channel : 7, programmed Period (uS): 125000.00, actual (uS) : 125001, programmed DutyCycle : 55.00, actual : 54.99 +PWM Channel : 8, programmed Period (uS): 111111.11, actual (uS) : 111120, programmed DutyCycle : 60.00, actual : 59.99 +PWM Channel : 9, programmed Period (uS): 100000.00, actual (uS) : 100000, programmed DutyCycle : 65.00, actual : 65.00 +PWM Channel : 10, programmed Period (uS): 66666.66, actual (uS) : 66680, programmed DutyCycle : 70.00, actual : 69.98 +PWM Channel : 11, programmed Period (uS): 50000.00, actual (uS) : 50000, programmed DutyCycle : 75.00, actual : 75.00 +PWM Channel : 12, programmed Period (uS): 40000.00, actual (uS) : 40000, programmed DutyCycle : 80.00, actual : 80.00 +PWM Channel : 13, programmed Period (uS): 33333.33, actual (uS) : 33340, programmed DutyCycle : 85.00, actual : 84.94 +PWM Channel : 14, programmed Period (uS): 25000.00, actual (uS) : 25000, programmed DutyCycle : 90.00, actual : 90.00 +PWM Channel : 15, programmed Period (uS): 20000.00, actual (uS) : 20000, programmed DutyCycle : 95.00, actual : 95.00 +SimpleTimer (ms): 2000, us : 22284001, Dus : 10167999 +PWM Channel : 0, programmed Period (uS): 1000000.00, actual (uS) : 1000000, programmed DutyCycle : 5.00, actual : 5.00 +PWM Channel : 1, programmed Period (uS): 500000.00, actual (uS) : 500000, programmed DutyCycle : 10.00, actual : 10.00 +PWM Channel : 2, programmed Period (uS): 333333.34, actual (uS) : 333340, programmed DutyCycle : 20.00, actual : 20.00 +PWM Channel : 3, programmed Period (uS): 250000.00, actual (uS) : 250000, programmed DutyCycle : 30.00, actual : 30.00 +PWM Channel : 4, programmed Period (uS): 200000.00, actual (uS) : 200000, programmed DutyCycle : 40.00, actual : 40.00 +PWM Channel : 5, programmed Period (uS): 166666.67, actual (uS) : 166680, programmed DutyCycle : 45.00, actual : 44.98 +PWM Channel : 6, programmed Period (uS): 142857.14, actual (uS) : 142860, programmed DutyCycle : 50.00, actual : 49.99 +PWM Channel : 7, programmed Period (uS): 125000.00, actual (uS) : 125000, programmed DutyCycle : 55.00, actual : 54.99 +PWM Channel : 8, programmed Period (uS): 111111.11, actual (uS) : 111120, programmed DutyCycle : 60.00, actual : 59.99 +PWM Channel : 9, programmed Period (uS): 100000.00, actual (uS) : 100000, programmed DutyCycle : 65.00, actual : 65.00 +PWM Channel : 10, programmed Period (uS): 66666.66, actual (uS) : 66679, programmed DutyCycle : 70.00, actual : 69.98 +PWM Channel : 11, programmed Period (uS): 50000.00, actual (uS) : 50000, programmed DutyCycle : 75.00, actual : 75.00 +PWM Channel : 12, programmed Period (uS): 40000.00, actual (uS) : 40000, programmed DutyCycle : 80.00, actual : 80.00 +PWM Channel : 13, programmed Period (uS): 33333.33, actual (uS) : 33341, programmed DutyCycle : 85.00, actual : 84.94 +PWM Channel : 14, programmed Period (uS): 25000.00, actual (uS) : 25001, programmed DutyCycle : 90.00, actual : 90.00 +PWM Channel : 15, programmed Period (uS): 20000.00, actual (uS) : 20000, programmed DutyCycle : 95.00, actual : 95.00 +``` -### Debug Terminal Output Samples +--- -### 1. PWMs_Array_Complex on NUCLEO_F767ZI +### 3. ISR_16_PWMs_Array_Complex on NUCLEO_L552ZE_Q -The following is the sample terminal output when running example [PWMs_Array_Complex](examples/PWMs_Array_Complex) on **NUCLEO_F767ZI** to demonstrate the accuracy of Hardware-based PWM, **especially when system is very busy**. +The following is the sample terminal output when running example [ISR_16_PWMs_Array_Complex](examples/ISR_16_PWMs_Array_Complex) on **NUCLEO_L552ZE_Q** to demonstrate how to use multiple PWM channels with complex callback functions, the accuracy of ISR Hardware PWM-channels, **especially when system is very busy**. The ISR PWM-channels is **running exactly according to corresponding programmed periods and duty-cycles** ``` -Starting PWMs_Array_Complex on NUCLEO_F767ZI -STM32_PWM v1.0.0 -Index = 0, Instance = 0x40000000, channel = 1, TimerIndex = 1, PinName = 0 -Index = 1, Instance = 0x40010000, channel = 3, TimerIndex = 0, PinName = 77 -Index = 2, Instance = 0x40000800, channel = 4, TimerIndex = 3, PinName = 63 -SimpleTimer (ms): 2000, us : 12025001, Dus : 10019423 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 100000, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 50000, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 22058001, Dus : 10033000 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 99999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 49999, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 32091001, Dus : 10033000 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 99999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 50000, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 42124001, Dus : 10033000 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 99999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 50000, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 52157001, Dus : 10033000 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 99999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 49999, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 49.93 -SimpleTimer (ms): 2000, us : 62190001, Dus : 10033000 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 99999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 50000, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 +Starting ISR_16_PWMs_Array_Complex on NUCLEO_L552ZE_Q +STM32_SLOW_PWM v1.2.3 +[PWM] STM32TimerInterrupt: Timer Input Freq (Hz) = 110000000 +[PWM] Frequency = 1000000.00 , _count = 20 +Starting ITimer OK, micros() = 2016141 +Channel : 0 Period : 1000000 OnTime : 50000 Start_Time : 2019722 +Channel : 1 Period : 500000 OnTime : 50000 Start_Time : 2019722 +Channel : 2 Period : 333333 OnTime : 66666 Start_Time : 2019722 +Channel : 3 Period : 250000 OnTime : 75000 Start_Time : 2019722 +Channel : 4 Period : 200000 OnTime : 80000 Start_Time : 2019722 +Channel : 5 Period : 166666 OnTime : 74999 Start_Time : 2019722 +Channel : 6 Period : 142857 OnTime : 71428 Start_Time : 2019722 +Channel : 7 Period : 125000 OnTime : 68750 Start_Time : 2019722 +Channel : 8 Period : 111111 OnTime : 66666 Start_Time : 2019722 +Channel : 9 Period : 100000 OnTime : 65000 Start_Time : 2019722 +Channel : 10 Period : 66666 OnTime : 46666 Start_Time : 2019722 +Channel : 11 Period : 50000 OnTime : 37500 Start_Time : 2019722 +Channel : 12 Period : 40000 OnTime : 32000 Start_Time : 2019722 +Channel : 13 Period : 33333 OnTime : 28333 Start_Time : 2019722 +Channel : 14 Period : 25000 OnTime : 22500 Start_Time : 2019722 +Channel : 15 Period : 20000 OnTime : 19000 Start_Time : 2019722 +SimpleTimer (ms): 2000, us : 12115022, Dus : 10095303 +PWM Channel : 0, programmed Period (uS): 1000000, actual (uS) : 1000000, programmed DutyCycle : 5, actual : 5.00 +PWM Channel : 1, programmed Period (uS): 500000, actual (uS) : 500003, programmed DutyCycle : 10, actual : 10.00 +PWM Channel : 2, programmed Period (uS): 333333, actual (uS) : 333339, programmed DutyCycle : 20, actual : 20.00 +PWM Channel : 3, programmed Period (uS): 250000, actual (uS) : 250005, programmed DutyCycle : 30, actual : 30.00 +PWM Channel : 4, programmed Period (uS): 200000, actual (uS) : 200007, programmed DutyCycle : 40, actual : 40.00 +PWM Channel : 5, programmed Period (uS): 166666, actual (uS) : 166680, programmed DutyCycle : 45, actual : 44.99 +PWM Channel : 6, programmed Period (uS): 142857, actual (uS) : 142858, programmed DutyCycle : 50, actual : 49.99 +PWM Channel : 7, programmed Period (uS): 125000, actual (uS) : 124991, programmed DutyCycle : 55, actual : 54.99 +PWM Channel : 8, programmed Period (uS): 111111, actual (uS) : 111121, programmed DutyCycle : 60, actual : 59.99 +PWM Channel : 9, programmed Period (uS): 100000, actual (uS) : 99988, programmed DutyCycle : 65, actual : 65.01 +PWM Channel : 10, programmed Period (uS): 66666, actual (uS) : 66680, programmed DutyCycle : 70, actual : 69.98 +PWM Channel : 11, programmed Period (uS): 50000, actual (uS) : 50020, programmed DutyCycle : 75, actual : 74.97 +PWM Channel : 12, programmed Period (uS): 40000, actual (uS) : 40001, programmed DutyCycle : 80, actual : 80.00 +PWM Channel : 13, programmed Period (uS): 33333, actual (uS) : 33340, programmed DutyCycle : 85, actual : 84.94 +PWM Channel : 14, programmed Period (uS): 25000, actual (uS) : 24999, programmed DutyCycle : 90, actual : 90.00 +PWM Channel : 15, programmed Period (uS): 20000, actual (uS) : 20020, programmed DutyCycle : 95, actual : 94.91 +SimpleTimer (ms): 2000, us : 22275008, Dus : 10159986 +PWM Channel : 0, programmed Period (uS): 1000000, actual (uS) : 1000000, programmed DutyCycle : 5, actual : 5.00 +PWM Channel : 1, programmed Period (uS): 500000, actual (uS) : 500003, programmed DutyCycle : 10, actual : 10.00 +PWM Channel : 2, programmed Period (uS): 333333, actual (uS) : 333339, programmed DutyCycle : 20, actual : 20.00 +PWM Channel : 3, programmed Period (uS): 250000, actual (uS) : 249995, programmed DutyCycle : 30, actual : 30.00 +PWM Channel : 4, programmed Period (uS): 200000, actual (uS) : 199993, programmed DutyCycle : 40, actual : 40.00 +PWM Channel : 5, programmed Period (uS): 166666, actual (uS) : 166680, programmed DutyCycle : 45, actual : 44.99 +PWM Channel : 6, programmed Period (uS): 142857, actual (uS) : 142860, programmed DutyCycle : 50, actual : 49.99 +PWM Channel : 7, programmed Period (uS): 125000, actual (uS) : 125002, programmed DutyCycle : 55, actual : 54.99 +PWM Channel : 8, programmed Period (uS): 111111, actual (uS) : 111120, programmed DutyCycle : 60, actual : 59.99 +PWM Channel : 9, programmed Period (uS): 100000, actual (uS) : 99999, programmed DutyCycle : 65, actual : 65.00 +PWM Channel : 10, programmed Period (uS): 66666, actual (uS) : 66680, programmed DutyCycle : 70, actual : 69.98 +PWM Channel : 11, programmed Period (uS): 50000, actual (uS) : 50000, programmed DutyCycle : 75, actual : 75.00 +PWM Channel : 12, programmed Period (uS): 40000, actual (uS) : 39997, programmed DutyCycle : 80, actual : 80.00 +PWM Channel : 13, programmed Period (uS): 33333, actual (uS) : 33340, programmed DutyCycle : 85, actual : 84.94 +PWM Channel : 14, programmed Period (uS): 25000, actual (uS) : 25000, programmed DutyCycle : 90, actual : 90.00 +PWM Channel : 15, programmed Period (uS): 20000, actual (uS) : 20000, programmed DutyCycle : 95, actual : 95.00 ``` --- -### 2. PWMs_Array_Complex on NUCLEO_H743ZI2 +### 4. ISR_16_PWMs_Array_Complex on BLUEPILL_F103CB + +The following is the sample terminal output when running example [ISR_16_PWMs_Array_Complex](examples/ISR_16_PWMs_Array_Complex) on **BLUEPILL_F103CB** to demonstrate how to use multiple PWM channels with complex callback functions, the accuracy of ISR Hardware PWM-channels, **especially when system is very busy**. The ISR PWM-channels is **running exactly according to corresponding programmed periods and duty-cycles** -The following is the sample terminal output when running example [PWMs_Array_Complex](examples/PWMs_Array_Complex) on **NUCLEO_H743ZI2** to demonstrate the accuracy of Hardware-based PWM, **especially when system is very busy**. ``` -Starting PWMs_Array_Complex on NUCLEO_H743ZI2 -STM32_PWM v1.0.0 -Index = 0, Instance = 0x40000000, channel = 1, TimerIndex = 1, PinName = 0 -Index = 1, Instance = 0x40010000, channel = 3, TimerIndex = 0, PinName = 77 -Index = 2, Instance = 0x40000800, channel = 4, TimerIndex = 3, PinName = 63 -SimpleTimer (ms): 2000, us : 12025000, Dus : 10019435 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 100000, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 50000, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 22058000, Dus : 10033000 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 99999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 49999, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 32091000, Dus : 10033000 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 100000, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 49999, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 +Starting ISR_16_PWMs_Array_Complex on BLUEPILL_F103CB +STM32_SLOW_PWM v1.2.3 +[PWM] STM32TimerInterrupt: Timer Input Freq (Hz) = 72000000 +[PWM] Frequency = 1000000.00 , _count = 20 +Starting ITimer OK, micros() = 3390333 +Channel : 0 Period : 1000000 OnTime : 50000 Start_Time : 3390427 +Channel : 1 Period : 500000 OnTime : 50000 Start_Time : 3390427 +Channel : 2 Period : 333333 OnTime : 66666 Start_Time : 3390427 +Channel : 3 Period : 250000 OnTime : 75000 Start_Time : 3390427 +Channel : 4 Period : 200000 OnTime : 80000 Start_Time : 3390427 +Channel : 5 Period : 166666 OnTime : 74999 Start_Time : 3390427 +Channel : 6 Period : 142857 OnTime : 71428 Start_Time : 3390427 +Channel : 7 Period : 125000 OnTime : 68750 Start_Time : 3390427 +Channel : 8 Period : 111111 OnTime : 66666 Start_Time : 3390427 +Channel : 9 Period : 100000 OnTime : 65000 Start_Time : 3390427 +Channel : 10 Period : 66666 OnTime : 46666 Start_Time : 3390427 +Channel : 11 Period : 50000 OnTime : 37500 Start_Time : 3390427 +Channel : 12 Period : 40000 OnTime : 32000 Start_Time : 3390427 +Channel : 13 Period : 33333 OnTime : 28333 Start_Time : 3390427 +Channel : 14 Period : 25000 OnTime : 22500 Start_Time : 3390427 +Channel : 15 Period : 20000 OnTime : 19000 Start_Time : 3390427 +SimpleTimer (ms): 2000, us : 13397013, Dus : 10006588 +PWM Channel : 0, programmed Period (uS): 1000000, actual (uS) : 1000000, programmed DutyCycle : 5, actual : 5.00 +PWM Channel : 1, programmed Period (uS): 500000, actual (uS) : 500003, programmed DutyCycle : 10, actual : 10.00 +PWM Channel : 2, programmed Period (uS): 333333, actual (uS) : 333342, programmed DutyCycle : 20, actual : 20.00 +PWM Channel : 3, programmed Period (uS): 250000, actual (uS) : 250006, programmed DutyCycle : 30, actual : 30.00 +PWM Channel : 4, programmed Period (uS): 200000, actual (uS) : 199999, programmed DutyCycle : 40, actual : 40.00 +PWM Channel : 5, programmed Period (uS): 166666, actual (uS) : 166679, programmed DutyCycle : 45, actual : 44.99 +PWM Channel : 6, programmed Period (uS): 142857, actual (uS) : 142865, programmed DutyCycle : 50, actual : 49.99 +PWM Channel : 7, programmed Period (uS): 125000, actual (uS) : 125000, programmed DutyCycle : 55, actual : 54.99 +PWM Channel : 8, programmed Period (uS): 111111, actual (uS) : 111121, programmed DutyCycle : 60, actual : 59.99 +PWM Channel : 9, programmed Period (uS): 100000, actual (uS) : 100003, programmed DutyCycle : 65, actual : 65.00 +PWM Channel : 10, programmed Period (uS): 66666, actual (uS) : 66678, programmed DutyCycle : 70, actual : 69.98 +PWM Channel : 11, programmed Period (uS): 50000, actual (uS) : 50006, programmed DutyCycle : 75, actual : 74.99 +PWM Channel : 12, programmed Period (uS): 40000, actual (uS) : 40008, programmed DutyCycle : 80, actual : 79.99 +PWM Channel : 13, programmed Period (uS): 33333, actual (uS) : 33338, programmed DutyCycle : 85, actual : 84.95 +PWM Channel : 14, programmed Period (uS): 25000, actual (uS) : 25011, programmed DutyCycle : 90, actual : 89.96 +PWM Channel : 15, programmed Period (uS): 20000, actual (uS) : 19984, programmed DutyCycle : 95, actual : 94.91 +SimpleTimer (ms): 2000, us : 23412013, Dus : 10015000 +PWM Channel : 0, programmed Period (uS): 1000000, actual (uS) : 1000000, programmed DutyCycle : 5, actual : 5.00 +PWM Channel : 1, programmed Period (uS): 500000, actual (uS) : 500003, programmed DutyCycle : 10, actual : 10.00 +PWM Channel : 2, programmed Period (uS): 333333, actual (uS) : 333342, programmed DutyCycle : 20, actual : 20.00 +PWM Channel : 3, programmed Period (uS): 250000, actual (uS) : 250005, programmed DutyCycle : 30, actual : 30.00 +PWM Channel : 4, programmed Period (uS): 200000, actual (uS) : 199999, programmed DutyCycle : 40, actual : 40.00 +PWM Channel : 5, programmed Period (uS): 166666, actual (uS) : 166679, programmed DutyCycle : 45, actual : 44.99 +PWM Channel : 6, programmed Period (uS): 142857, actual (uS) : 142865, programmed DutyCycle : 50, actual : 49.99 +PWM Channel : 7, programmed Period (uS): 125000, actual (uS) : 125020, programmed DutyCycle : 55, actual : 54.98 +PWM Channel : 8, programmed Period (uS): 111111, actual (uS) : 111121, programmed DutyCycle : 60, actual : 59.99 +PWM Channel : 9, programmed Period (uS): 100000, actual (uS) : 100023, programmed DutyCycle : 65, actual : 64.99 +PWM Channel : 10, programmed Period (uS): 66666, actual (uS) : 66678, programmed DutyCycle : 70, actual : 69.98 +PWM Channel : 11, programmed Period (uS): 50000, actual (uS) : 50025, programmed DutyCycle : 75, actual : 74.96 +PWM Channel : 12, programmed Period (uS): 40000, actual (uS) : 40028, programmed DutyCycle : 80, actual : 79.90 +PWM Channel : 13, programmed Period (uS): 33333, actual (uS) : 33338, programmed DutyCycle : 85, actual : 84.97 +PWM Channel : 14, programmed Period (uS): 25000, actual (uS) : 24988, programmed DutyCycle : 90, actual : 90.03 +PWM Channel : 15, programmed Period (uS): 20000, actual (uS) : 19984, programmed DutyCycle : 95, actual : 95.00 ``` --- -### 3. PWMs_Array_Complex on NUCLEO_L552ZE_Q +### 5. ISR_Modify_PWM on NUCLEO_F767ZI -The following is the sample terminal output when running example [PWMs_Array_Complex](examples/PWMs_Array_Complex) on **NUCLEO_L552ZE_Q** to demonstrate the accuracy of Hardware-based PWM, **especially when system is very busy**. +The following is the sample terminal output when running example [ISR_Modify_PWM](examples/ISR_Modify_PWM) on **NUCLEO_F767ZI** to demonstrate how to modify PWM settings on-the-fly without deleting the PWM channel ``` -Starting PWMs_Array_Complex on NUCLEO_L552ZE_Q -STM32_PWM v1.0.0 -Index = 0, Instance = 0x40000000, channel = 1, TimerIndex = 1, PinName = 0 -Index = 1, Instance = 0x40012C00, channel = 3, TimerIndex = 0, PinName = 77 -Index = 2, Instance = 0x40000800, channel = 4, TimerIndex = 3, PinName = 63 -SimpleTimer (ms): 2000, us : 12026006, Dus : 10020290 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 99999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 49999, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 22060006, Dus : 10034000 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 99999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 50000, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 32094006, Dus : 10034000 -PWM Channel : 0100000, programmed Period (us): 100000, actual : 100000, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 150000, programmed Period (us): 50000, actual : 50000, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 22000, programmed Period (us): 2000, actual : 1999, programmed DutyCycle : 50, actual : 50.00 +Starting ISR_Modify_PWM on NUCLEO_F767ZI +STM32_SLOW_PWM v1.2.3 +[PWM] STM32TimerInterrupt: Timer Input Freq (Hz) = 216000000 , Timer Clock Frequency = 1000000.00 +[PWM] Timer Frequency = 50000.00 , _count = 20 +Starting ITimer OK, micros() = 2016546 +Using PWM Freq = 200.00, PWM DutyCycle = 1.00 +Channel : 0 Period : 5000 OnTime : 50 Start_Time : 2022139 +Channel : 0 Period : 10000 OnTime : 555 Start_Time : 12027668 +Channel : 0 Period : 5000 OnTime : 50 Start_Time : 22022668 +Channel : 0 Period : 10000 OnTime : 555 Start_Time : 32027668 +Channel : 0 Period : 5000 OnTime : 50 Start_Time : 42022668 +Channel : 0 Period : 10000 OnTime : 555 Start_Time : 52027668 +Channel : 0 Period : 5000 OnTime : 50 Start_Time : 62032668 +Channel : 0 Period : 10000 OnTime : 555 Start_Time : 72032668 +Channel : 0 Period : 5000 OnTime : 50 Start_Time : 82027668 +Channel : 0 Period : 10000 OnTime : 555 Start_Time : 92032668 +Channel : 0 Period : 5000 OnTime : 50 Start_Time : 102027668 +Channel : 0 Period : 10000 OnTime : 555 Start_Time : 112037668 +Channel : 0 Period : 5000 OnTime : 50 Start_Time : 122032668 +Channel : 0 Period : 10000 OnTime : 555 Start_Time : 132037668 +Channel : 0 Period : 5000 OnTime : 50 Start_Time : 142032668 ``` --- -### 4. PWMs_Array_Complex on BLUEPILL_F103CB +### 6. ISR_Changing_PWM on NUCLEO_F767ZI -The following is the sample terminal output when running example [PWMs_Array_Complex](examples/PWMs_Array_Complex) on **BLUEPILL_F103CB** to demonstrate the accuracy of Hardware-based PWM, **especially when system is very busy**. +The following is the sample terminal output when running example [ISR_Changing_PWM](examples/ISR_Changing_PWM) on **NUCLEO_F767ZI** to demonstrate how to modify PWM settings on-the-fly by deleting the PWM channel and reinit the PWM channel ``` -Starting PWMs_Array_Complex on BLUEPILL_F103CB -STM32_PWM v1.0.0 -Using pin = 0, 1, etc => Index = 0, Instance = 0x40000000, channel = 1, TimerIndex = 1, PinName = 0 -Using pin = 0, 1, etc => Index = 1, Instance = 0x40000400, channel = 2, TimerIndex = 2, PinName = 21 -Using pin = 0, 1, etc => Index = 2, Instance = 0x40012C00, channel = 3, TimerIndex = 0, PinName = 10 -SimpleTimer (ms): 2000, us : 12970007, Dus : 9999926 -PWM Channel : 0, programmed Period (us): 50000, actual : 50000, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 1, programmed Period (us): 20000, actual : 20000, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 2, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 22971007, Dus : 10001000 -PWM Channel : 0, programmed Period (us): 50000, actual : 49999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 1, programmed Period (us): 20000, actual : 20000, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 2, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 32972007, Dus : 10001000 -PWM Channel : 0, programmed Period (us): 50000, actual : 49999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 1, programmed Period (us): 20000, actual : 20000, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 2, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 42973007, Dus : 10001000 -PWM Channel : 0, programmed Period (us): 50000, actual : 49999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 1, programmed Period (us): 20000, actual : 19999, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 2, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 -SimpleTimer (ms): 2000, us : 52974007, Dus : 10001000 -PWM Channel : 0, programmed Period (us): 50000, actual : 49999, programmed DutyCycle : 20, actual : 20.00 -PWM Channel : 1, programmed Period (us): 20000, actual : 20000, programmed DutyCycle : 30, actual : 30.00 -PWM Channel : 2, programmed Period (us): 2000, actual : 2000, programmed DutyCycle : 50, actual : 50.00 +Starting ISR_Changing_PWM on NUCLEO_F767ZI +STM32_SLOW_PWM v1.2.3 +[PWM] STM32TimerInterrupt: Timer Input Freq (Hz) = 216000000 , Timer Clock Frequency = 1000000.00 +[PWM] Timer Frequency = 50000.00 , _count = 20 +Starting ITimer OK, micros() = 2016548 +Using PWM Freq = 1.00, PWM DutyCycle = 50.00 +Channel : 0 Period : 1000000 OnTime : 500000 Start_Time : 2022140 +Using PWM Freq = 2.00, PWM DutyCycle = 90.00 +Channel : 0 Period : 500000 OnTime : 450000 Start_Time : 12027037 +Using PWM Freq = 1.00, PWM DutyCycle = 50.00 +Channel : 0 Period : 1000000 OnTime : 500000 Start_Time : 22032037 +Using PWM Freq = 2.00, PWM DutyCycle = 90.00 +Channel : 0 Period : 500000 OnTime : 450000 Start_Time : 32038037 +``` + --- --- @@ -961,7 +757,7 @@ Sometimes, the library will only work if you update the board core to the latest ### Issues -Submit issues to: [STM32_PWM issues](https://github.com/khoih-prog/STM32_PWM/issues) +Submit issues to: [STM32_Slow_PWM issues](https://github.com/khoih-prog/STM32_Slow_PWM/issues) --- @@ -974,8 +770,16 @@ Submit issues to: [STM32_PWM issues](https://github.com/khoih-prog/STM32_PWM/iss ## DONE -1. Basic hardware-based multi-channel PWM for **STM32F/L/H/G/WB/MP1 boards**. -2. Add Table of Contents + 1. Basic hardware multi-channel PWM for **STM32F/L/H/G/WB/MP1 boards** such as NUCLEO_H743ZI2, NUCLEO_L552ZE_Q, NUCLEO_F767ZI, BLUEPILL_F103CB, etc., using [`Arduino Core for STM32`](https://github.com/stm32duino/Arduino_Core_STM32) + 2. Add Table of Contents + 3. Add functions to modify PWM settings on-the-fly + 4. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories + 5. Add example [multiFileProject](examples/multiFileProject) to demo for multiple-file project + 6. Improve accuracy by using `float`, instead of `uint32_t` for `dutycycle` + 7. Optimize library code by using `reference-passing` instead of `value-passing` + 8. Fix reattachInterrupt() bug. Check [bugfix: reattachInterrupt() pass wrong frequency value to setFrequency() #19](https://github.com/khoih-prog/ESP8266TimerInterrupt/pull/19) + 9. DutyCycle to be optionally updated at the end current PWM period instead of immediately. +10. Display informational warning only when `_PWM_LOGLEVEL_` > 3 --- --- @@ -999,7 +803,7 @@ If you want to contribute to this project: ### License -- The library is licensed under [MIT](https://github.com/khoih-prog/STM32_PWM/blob/master/LICENSE) +- The library is licensed under [MIT](https://github.com/khoih-prog/STM32_Slow_PWM/blob/master/LICENSE) --- diff --git a/changelog.md b/changelog.md index 54c89fe..5cea89f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,10 @@ -# STM32_PWM Library +# STM32_Slow_PWM Library -[![arduino-library-badge](https://www.ardu-badge.com/badge/STM32_PWM.svg?)](https://www.ardu-badge.com/STM32_PWM) -[![GitHub release](https://img.shields.io/github/release/khoih-prog/STM32_PWM.svg)](https://github.com/khoih-prog/STM32_PWM/releases) -[![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/STM32_PWM/blob/master/LICENSE) +[![arduino-library-badge](https://www.ardu-badge.com/badge/STM32_Slow_PWM.svg?)](https://www.ardu-badge.com/STM32_Slow_PWM) +[![GitHub release](https://img.shields.io/github/release/khoih-prog/STM32_Slow_PWM.svg)](https://github.com/khoih-prog/STM32_Slow_PWM/releases) +[![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/STM32_Slow_PWM/blob/master/LICENSE) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) -[![GitHub issues](https://img.shields.io/github/issues/khoih-prog/STM32_PWM.svg)](http://github.com/khoih-prog/STM32_PWM/issues) +[![GitHub issues](https://img.shields.io/github/issues/khoih-prog/STM32_Slow_PWM.svg)](http://github.com/khoih-prog/STM32_Slow_PWM/issues) --- --- @@ -12,6 +12,11 @@ ## Table of Contents * [Changelog](#changelog) + * [Releases v1.2.3](#releases-v123) + * [Releases v1.2.2](#releases-v122) + * [Releases v1.2.1](#releases-v121) + * [Releases v1.2.0](#releases-v120) + * [Releases v1.1.0](#releases-v110) * [Initial Releases v1.0.0](#Initial-Releases-v100) --- @@ -19,11 +24,39 @@ ## Changelog +### Releases v1.2.3 + +1. Fix `DutyCycle` bug. Check [float precisison of DutyCycle only sometimes working #3](https://github.com/khoih-prog/SAMD_Slow_PWM/issues/3) +2. Fix `New Period` display bug. Check [random dropouts #4](https://github.com/khoih-prog/SAMD_Slow_PWM/issues/4) +3. Update examples + +### Releases v1.2.2 + +1. Use `float` for `DutyCycle` and `Freq`, `uint32_t` for `period`. +2. Optimize code by not calculation in ISR + +### Releases v1.2.1 + +1. DutyCycle to be optionally updated at the end current PWM period instead of immediately. Check [DutyCycle to be updated at the end current PWM period #2](https://github.com/khoih-prog/ESP8266_PWM/issues/2) + +### Releases v1.2.0 + +1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories +2. Add example [multiFileProject](examples/multiFileProject) to demo for multiple-file project +3. Improve accuracy by using `double`, instead of `uint32_t` for `dutycycle`, `period` +4. Optimize library code by using `reference-passing` instead of `value-passing` +5. Fix reattachInterrupt() bug. Check [bugfix: reattachInterrupt() pass wrong frequency value to setFrequency() #19](https://github.com/khoih-prog/ESP8266TimerInterrupt/pull/19) +6. Update examples accordingly + +### Releases v1.1.0 + +1. Add functions to modify PWM settings on-the-fly +2. Add example to demo how to modify PWM settings on-the-fly + ### Initial Releases v1.0.0 1. Initial coding to support **STM32F/L/H/G/WB/MP1 boards** such as NUCLEO_H743ZI2, NUCLEO_L552ZE_Q, NUCLEO_F767ZI, BLUEPILL_F103CB, etc., using [`Arduino Core for STM32`](https://github.com/stm32duino/Arduino_Core_STM32) -2. The hardware-based PWM channels can generate very high PWM frequencies up with high accuracy. - +2. The hybrid ISR-based PWM channels can generate from very low (much less than 1Hz) to highest PWM frequencies up to 1000Hz with acceptable accuracy. --- --- diff --git a/keywords.txt b/keywords.txt index 4dce44c..af2b210 100644 --- a/keywords.txt +++ b/keywords.txt @@ -2,65 +2,63 @@ # Datatypes (KEYWORD1) ####################################### -HardwareTimer KEYWORD1 -callback_function_t KEYWORD1 - -timer_index_t KEYWORD1 -timerObj_t KEYWORD1 +STM32TimerInterrupt KEYWORD1 +STM32Timer KEYWORD1 +STM32_SLOW_PWM_ISR KEYWORD1 +STM32_Slow_PWM KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -################################### -# Class HardwareTimer -################################### +################################## +# class STM32TimerInterrupt +################################## -pause KEYWORD2 -pauseChannel KEYWORD2 -resume KEYWORD2 -resumeChannel KEYWORD2 -setPrescaleFactor KEYWORD2 -getPrescaleFactor KEYWORD2 -setOverflow KEYWORD2 -getOverflow KEYWORD2 -setPWM KEYWORD2 -setCount KEYWORD2 -getCount KEYWORD2 -setMode KEYWORD2 -getMode KEYWORD2 -setPreloadEnable KEYWORD2 -getCaptureCompare KEYWORD2 -setCaptureCompare KEYWORD2 -setInterruptPriority KEYWORD2 +setFrequency KEYWORD2 +setInterval KEYWORD2 attachInterrupt KEYWORD2 +attachInterruptInterval KEYWORD2 detachInterrupt KEYWORD2 -hasInterrupt KEYWORD2 -timerHandleDeinit KEYWORD2 -refresh KEYWORD2 -getTimerClkFreq KEYWORD2 -captureCompareCallback KEYWORD2 -updateCallback KEYWORD2 -getHandle KEYWORD2 -getChannel KEYWORD2 -getLLChannel KEYWORD2 -getIT KEYWORD2 -getAssociatedChannel KEYWORD2 -isComplementaryChannel KEYWORD2 - -################################### -# timer.h/c -################################### +disableTimer KEYWORD2 +reattachInterrupt KEYWORD2 +enableTimer KEYWORD2 +stopTimer KEYWORD2 +restartTimer KEYWORD2 -get_timer_obj KEYWORD2 -enableTimerClock KEYWORD2 -disableTimerClock KEYWORD2 -getTimerIrq KEYWORD2 -getTimerClkSrc KEYWORD2 -getTimerUpIrq KEYWORD2 -getTimerCCIrq KEYWORD2 +################################# +# class STM32_SLOW_PWM_ISR +################################# +init KEYWORD2 +run KEYWORD2 +setPWM KEYWORD2 +setPWM_Period KEYWORD2 +modifyPWMChannel KEYWORD2 +modifyPWMChannel_Period KEYWORD2 +deleteChannel KEYWORD2 +restartChannel KEYWORD2 +isEnabled KEYWORD2 +enable KEYWORD2 +disable KEYWORD2 +enableAll KEYWORD2 +disableAll KEYWORD2 +toggle KEYWORD2 +getnumChannels KEYWORD2 +getNumAvailablePWMChannels KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### + +STM32_SLOW_PWM_VERSION LITERAL1 +STM32_SLOW_PWM_VERSION_MAJOR LITERAL1 +STM32_SLOW_PWM_VERSION_MINOR LITERAL1 +STM32_SLOW_PWM_VERSION_PATCH LITERAL1 +STM32_SLOW_PWM_VERSION_INT LITERAL1 + +INVALID_STM32_PIN LITERAL1 + +USING_MICROS_RESOLUTION LITERAL1 +CHANGING_PWM_END_OF_CYCLE LITERAL1 + diff --git a/library.json b/library.json index 551a780..30e44cd 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { - "name": "STM32_PWM", - "version": "1.0.0", + "name": "STM32_Slow_PWM", + "version": "1.2.3", "keywords": "timing, device, control, timer, pwm, interrupt, isr, isr-based, hardware-timer, mission-critical, accuracy, non-blocking, stm32, stm32h7, stm32l5, stm32f1, stm32f4, stm32f7, stm32g4, precise, hardware", - "description": "This wrapper library enables you to use Hardware-based PWM on STM32F/L/H/G/WB/MP1 boards to create and output PWM to pins. The most important feature is they're purely hardware-based PWM channels. Therefore, their executions are very precise and not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware PWM channels still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other ISR-based or software-based PWM using millis() or micros(). That's necessary if you need to measure some data requiring very high frequency and much better accuracy. PWM feature can now be used.", + "description": "This library enables you to use Hardware Timers on STM32F/L/H/G/WB/MP1 boards to create and output PWM to pins. The most important feature is they're purely hardware-based PWM channels. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware timers, using interrupt, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy. PWM feature can now be used. Max PWM frequency is limited at 1000Hz. Now you can change the PWM settings on-the-fly", "authors": { "name": "Khoi Hoang", @@ -12,9 +12,9 @@ "repository": { "type": "git", - "url": "https://github.com/khoih-prog/STM32_PWM" + "url": "https://github.com/khoih-prog/STM32_Slow_PWM.git" }, - "homepage": "https://github.com/khoih-prog/STM32_PWM", + "homepage": "https://github.com/khoih-prog/STM32_Slow_PWM", "export": { "exclude": [ "linux", @@ -22,8 +22,9 @@ "tests" ] }, + "license": "MIT", "frameworks": "*", "platforms": "ststm32", "examples": "examples/*/*/*.ino", - "license": "MIT" + "headers": ["STM32_Slow_PWM.h", "STM32_Slow_PWM.hpp"] } diff --git a/library.properties b/library.properties index b14f2f2..cb0ad67 100644 --- a/library.properties +++ b/library.properties @@ -1,12 +1,12 @@ -name=STM32_PWM -version=1.0.0 +name=STM32_Slow_PWM +version=1.2.3 author=Khoi Hoang maintainer=Khoi Hoang -sentence=This wrapper library enables you to use Hardware-based PWM on STM32F/L/H/G/WB/MP1 boards to create and output PWM to pins. -paragraph=The most important feature is they're purely hardware-based PWM channels. Therefore, their executions are very precise and not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware PWM channels still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other ISR-based or software-based PWM using millis() or micros(). That's necessary if you need to measure some data requiring very high frequency and much better accuracy. PWM feature can now be used. +sentence=This library enables you to use Hardware Timers on STM32F/L/H/G/WB/MP1 boards to create and output PWM to pins. +paragraph=These PWM channels, using STM32 Hardware Timers, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That is mandatory if you need to measure some data requiring better accuracy. It now supports 16 ISR-based Timers, while consuming only 1 Hardware Timer. Timers interval is very long (ulong millisecs). The most important feature is they are ISR-based Timers. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. Max PWM frequency is limited at 1000Hz. Now you can change the PWM settings on-the-fly category=Device Control -url=https://github.com/khoih-prog/STM32_PWM +url=https://github.com/khoih-prog/STM32_Slow_PWM architectures=stm32 -repository=https://github.com/khoih-prog/STM32_PWM +repository=https://github.com/khoih-prog/STM32_Slow_PWM license=MIT -includes=STM32_PWM.h +includes=STM32_Slow_PWM.h,STM32_Slow_PWM.hpp diff --git a/src/PWM_Generic_Debug.h b/src/PWM_Generic_Debug.h index 7a5cb6e..d416439 100644 --- a/src/PWM_Generic_Debug.h +++ b/src/PWM_Generic_Debug.h @@ -3,16 +3,25 @@ For STM32F/L/H/G/WB/MP1 boards Written by Khoi Hoang - Built by Khoi Hoang https://github.com/khoih-prog/STM32_PWM + Built by Khoi Hoang https://github.com/khoih-prog/STM32_Slow_PWM Licensed under MIT license - - Hardware-based multi-channel PWM wrapper library for STM32F/L/H/G/WB/MP1 boards - Version: 1.0.0 + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one STM32 timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Version: 1.2.3 Version Modified By Date Comments ------- ----------- ---------- ----------- - 1.0.0 K.Hoang 30/09/2021 Initial coding for STM32F/L/H/G/WB/MP1 boards + 1.0.0 K.Hoang 22/09/2021 Initial coding for STM32F/L/H/G/WB/MP1 + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly + 1.2.0 K Hoang 29/01/2022 Fix multiple-definitions linker error. Improve accuracy + 1.2.1 K Hoang 30/01/2022 DutyCycle to be updated at the end current PWM period + 1.2.2 K Hoang 01/02/2022 Use float for DutyCycle and Freq, uint32_t for period. Optimize code + 1.2.3 K Hoang 03/03/2022 Fix `DutyCycle` and `New Period` display bugs. Display warning only when debug level > 3 *****************************************************************************************************************************/ #pragma once @@ -37,32 +46,54 @@ #define _PWM_LOGLEVEL_ 1 #endif -#define PWM_LOGERROR(x) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.println(x); } -#define PWM_LOGERROR0(x) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.print(x); } -#define PWM_LOGERRORLN0(x) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.println(x); } -#define PWM_LOGERROR1(x,y) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(y); } -#define PWM_LOGERROR2(x,y,z) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(z); } -#define PWM_LOGERROR3(x,y,z,w) if(_PWM_LOGLEVEL_>0) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(z); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(w); } - -#define PWM_LOGWARN(x) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.println(x); } -#define PWM_LOGWARN0(x) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.print(x); } -#define PWM_LOGWARNLN0(x) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.println(x); } -#define PWM_LOGWARN1(x,y) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(y); } -#define PWM_LOGWARN2(x,y,z) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(z); } -#define PWM_LOGWARN3(x,y,z,w) if(_PWM_LOGLEVEL_>1) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(z); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(w); } - -#define PWM_LOGINFO(x) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.println(x); } -#define PWM_LOGINFO0(x) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.print(x); } -#define PWM_LOGINFOLN0(x) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.println(x); } -#define PWM_LOGINFO1(x,y) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(y); } -#define PWM_LOGINFO2(x,y,z) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(z); } -#define PWM_LOGINFO3(x,y,z,w) if(_PWM_LOGLEVEL_>2) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(z); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(w); } - -#define PWM_LOGDEBUG(x) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.println(x); } -#define PWM_LOGDEBUG0(x) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.print(x); } -#define PWM_LOGDEBUGLN0(x) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.println(x); } -#define PWM_LOGDEBUG1(x,y) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(y); } -#define PWM_LOGDEBUG2(x,y,z) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(z); } -#define PWM_LOGDEBUG3(x,y,z,w) if(_PWM_LOGLEVEL_>3) { PWM_DBG_PORT.print("[PWM] "); PWM_DBG_PORT.print(x); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(y); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.print(z); PWM_DBG_PORT.print(" "); PWM_DBG_PORT.println(w); } +/////////////////////////////////////// + +const char PWM_MARK[] = "[PWM] "; +const char PWM_SPACE[] = " "; + +#define PWM_PRINT PWM_DBG_PORT.print +#define PWM_PRINTLN PWM_DBG_PORT.println + +#define PWM_PRINT_MARK PWM_PRINT(PWM_MARK) +#define PWM_PRINT_SP PWM_PRINT(PWM_SPACE) +#define PWM_PRINT_LINE PWM_PRINT(PWM_LINE) + +/////////////////////////////////////// + +#define PWM_LOGERROR(x) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINTLN(x); } +#define PWM_LOGERROR0(x) if(_PWM_LOGLEVEL_>0) { PWM_PRINT(x); } +#define PWM_LOGERRORLN0(x) if(_PWM_LOGLEVEL_>0) { PWM_PRINTLN(x); } +#define PWM_LOGERROR1(x,y) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINTLN(y); } +#define PWM_LOGERROR2(x,y,z) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); } +#define PWM_LOGERROR3(x,y,z,w) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); } + +/////////////////////////////////////// + +#define PWM_LOGWARN(x) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINTLN(x); } +#define PWM_LOGWARN0(x) if(_PWM_LOGLEVEL_>1) { PWM_PRINT(x); } +#define PWM_LOGWARNLN0(x) if(_PWM_LOGLEVEL_>1) { PWM_PRINTLN(x); } +#define PWM_LOGWARN1(x,y) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINTLN(y); } +#define PWM_LOGWARN2(x,y,z) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); } +#define PWM_LOGWARN3(x,y,z,w) if(_PWM_LOGLEVEL_>1) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); } + +/////////////////////////////////////// + +#define PWM_LOGINFO(x) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINTLN(x); } +#define PWM_LOGINFO0(x) if(_PWM_LOGLEVEL_>2) { PWM_PRINT(x); } +#define PWM_LOGINFOLN0(x) if(_PWM_LOGLEVEL_>2) { PWM_PRINTLN(x); } +#define PWM_LOGINFO1(x,y) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINTLN(y); } +#define PWM_LOGINFO2(x,y,z) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); } +#define PWM_LOGINFO3(x,y,z,w) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); } + +/////////////////////////////////////// + +#define PWM_LOGDEBUG(x) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINTLN(x); } +#define PWM_LOGDEBUG0(x) if(_PWM_LOGLEVEL_>3) { PWM_PRINT(x); } +#define PWM_LOGDEBUGLN0(x) if(_PWM_LOGLEVEL_>3) { PWM_PRINTLN(x); } +#define PWM_LOGDEBUG1(x,y) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINTLN(y); } +#define PWM_LOGDEBUG2(x,y,z) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); } +#define PWM_LOGDEBUG3(x,y,z,w) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); } + +/////////////////////////////////////// #endif //PWM_GENERIC_DEBUG_H diff --git a/src/STM32_Slow_PWM.h b/src/STM32_Slow_PWM.h new file mode 100644 index 0000000..9ad81a1 --- /dev/null +++ b/src/STM32_Slow_PWM.h @@ -0,0 +1,37 @@ +/**************************************************************************************************************************** + STM32_Slow_PWM.h + For STM32F/L/H/G/WB/MP1 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/STM32_Slow_PWM + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one STM32 timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Version: 1.2.3 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K.Hoang 22/09/2021 Initial coding for STM32F/L/H/G/WB/MP1 + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly + 1.2.0 K Hoang 29/01/2022 Fix multiple-definitions linker error. Improve accuracy + 1.2.1 K Hoang 30/01/2022 DutyCycle to be updated at the end current PWM period + 1.2.2 K Hoang 01/02/2022 Use float for DutyCycle and Freq, uint32_t for period. Optimize code + 1.2.3 K Hoang 03/03/2022 Fix `DutyCycle` and `New Period` display bugs. Display warning only when debug level > 3 +*****************************************************************************************************************************/ + +#pragma once + +#ifndef STM32_SLOW_PWM_H +#define STM32_SLOW_PWM_H + +#include "STM32_Slow_PWM.hpp" + +#include "STM32_Slow_PWM_ISR.h" + +#endif // STM32_SLOW_PWM_H + diff --git a/src/STM32_Slow_PWM.hpp b/src/STM32_Slow_PWM.hpp new file mode 100644 index 0000000..9801cd2 --- /dev/null +++ b/src/STM32_Slow_PWM.hpp @@ -0,0 +1,180 @@ +/**************************************************************************************************************************** + STM32_Slow_PWM.hpp + For STM32F/L/H/G/WB/MP1 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/STM32_Slow_PWM + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one STM32 timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Version: 1.2.3 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K.Hoang 22/09/2021 Initial coding for STM32F/L/H/G/WB/MP1 + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly + 1.2.0 K Hoang 29/01/2022 Fix multiple-definitions linker error. Improve accuracy + 1.2.1 K Hoang 30/01/2022 DutyCycle to be updated at the end current PWM period + 1.2.2 K Hoang 01/02/2022 Use float for DutyCycle and Freq, uint32_t for period. Optimize code + 1.2.3 K Hoang 03/03/2022 Fix `DutyCycle` and `New Period` display bugs. Display warning only when debug level > 3 +*****************************************************************************************************************************/ + +#pragma once + +#ifndef STM32_SLOW_PWM_HPP +#define STM32_SLOW_PWM_HPP + +#if defined(ARDUINO) + #if ARDUINO >= 100 + #include + #else + #include + #endif +#endif + +#if !( defined(STM32F0) || defined(STM32F1) || defined(STM32F2) || defined(STM32F3) ||defined(STM32F4) || defined(STM32F7) || \ + defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32H7) ||defined(STM32G0) || defined(STM32G4) || \ + defined(STM32WB) || defined(STM32MP1) || defined(STM32L5)) + #error This code is designed to run on STM32F/L/H/G/WB/MP1 platform! Please check your Tools->Board setting. +#endif + +#ifndef STM32_SLOW_PWM_VERSION + #define STM32_SLOW_PWM_VERSION "STM32_SLOW_PWM v1.2.3" + + #define STM32_SLOW_PWM_VERSION_MAJOR 1 + #define STM32_SLOW_PWM_VERSION_MINOR 2 + #define STM32_SLOW_PWM_VERSION_PATCH 3 + + #define STM32_SLOW_PWM_VERSION_INT 1002003 +#endif + +/////////////////////////////////////////// + + +#include "PWM_Generic_Debug.h" + +class STM32TimerInterrupt; + +typedef STM32TimerInterrupt STM32Timer; + +typedef void (*timerCallback) (); + + +class STM32TimerInterrupt +{ + private: + TIM_TypeDef* _timer; + HardwareTimer* _hwTimer = NULL; + + timerCallback _callback; // pointer to the callback function + float _frequency; // Timer frequency + uint32_t _timerCount; // count to activate timer + + public: + + STM32TimerInterrupt(TIM_TypeDef* timer) + { + _timer = timer; + + _hwTimer = new HardwareTimer(_timer); + + _callback = NULL; + }; + + ~STM32TimerInterrupt() + { + if (_hwTimer) + delete _hwTimer; + } + + // frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely + // No params and duration now. To be addes in the future by adding similar functions here or to STM32-hal-timer.c + bool setFrequency(const float& frequency, timerCallback callback) + { + // select timer frequency is 1MHz for better accuracy. We don't use 16-bit prescaler for now. + // Will use later if very low frequency is needed. + #define TIM_CLOCK_FREQ (1000000.0f) + + _frequency = frequency; + + _timerCount = (uint32_t) ( TIM_CLOCK_FREQ / frequency ); + + PWM_LOGWARN3(F("STM32TimerInterrupt: Timer Input Freq (Hz) ="), _hwTimer->getTimerClkFreq(), F(", Timer Clock Frequency ="), TIM_CLOCK_FREQ); + PWM_LOGWARN3(F("Timer Frequency ="), _frequency, F(", _count ="), (uint32_t) (_timerCount)); + + _hwTimer->setCount(0, MICROSEC_FORMAT); + _hwTimer->setOverflow(_timerCount, MICROSEC_FORMAT); + + _hwTimer->attachInterrupt(callback); + _hwTimer->resume(); + + return true; + } + + // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely + // No params and duration now. To be addes in the future by adding similar functions here or to STM32-hal-timer.c + bool setInterval(const unsigned long& interval, timerCallback callback) + { + return setFrequency((float) (1000000.0f / interval), callback); + } + + bool attachInterrupt(const float& frequency, timerCallback callback) + { + return setFrequency(frequency, callback); + } + + // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely + // No params and duration now. To be addes in the future by adding similar functions here or to STM32-hal-timer.c + bool attachInterruptInterval(const unsigned long& interval, timerCallback callback) + { + return setFrequency( (float) ( 1000000.0f / interval), callback); + } + + void detachInterrupt() + { + _hwTimer->detachInterrupt(); + } + + void disableTimer() + { + //_hwTimer->detachInterrupt(); + _hwTimer->pause(); + } + + // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely + void reattachInterrupt() + { + if ( (_frequency > 0) && (_timerCount > 0) && (_callback != NULL) ) + setFrequency(_frequency, _callback); + } + + // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely + void enableTimer() + { + //setFrequency(_frequency, _callback); + _hwTimer->setCount(0, MICROSEC_FORMAT); + _hwTimer->resume(); + } + + // Just stop clock source, clear the count + void stopTimer() + { + _hwTimer->pause(); + _hwTimer->setCount(0, MICROSEC_FORMAT); + } + + // Just reconnect clock source, start current count from 0 + void restartTimer() + { + _hwTimer->setCount(0, MICROSEC_FORMAT); + _hwTimer->resume(); + } +}; // class STM32TimerInterrupt + +#endif // STM32_SLOW_PWM_HPP + diff --git a/src/STM32_Slow_PWM_ISR.h b/src/STM32_Slow_PWM_ISR.h new file mode 100644 index 0000000..9603034 --- /dev/null +++ b/src/STM32_Slow_PWM_ISR.h @@ -0,0 +1,38 @@ +/**************************************************************************************************************************** + STM32_Slow_PWM_ISR.h + For STM32F/L/H/G/WB/MP1 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/STM32_Slow_PWM + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one STM32 timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Version: 1.2.3 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K.Hoang 22/09/2021 Initial coding for STM32F/L/H/G/WB/MP1 + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly + 1.2.0 K Hoang 29/01/2022 Fix multiple-definitions linker error. Improve accuracy + 1.2.1 K Hoang 30/01/2022 DutyCycle to be updated at the end current PWM period + 1.2.2 K Hoang 01/02/2022 Use float for DutyCycle and Freq, uint32_t for period. Optimize code + 1.2.3 K Hoang 03/03/2022 Fix `DutyCycle` and `New Period` display bugs. Display warning only when debug level > 3 +*****************************************************************************************************************************/ + +#pragma once + +#ifndef STM32_SLOW_PWM_ISR_H +#define STM32_SLOW_PWM_ISR_H + + +#include "STM32_Slow_PWM_ISR.hpp" +#include "STM32_Slow_PWM_ISR_Impl.h" + +#endif // STM32_SLOW_PWM_ISR_H + + diff --git a/src/STM32_Slow_PWM_ISR.hpp b/src/STM32_Slow_PWM_ISR.hpp new file mode 100644 index 0000000..ad2f327 --- /dev/null +++ b/src/STM32_Slow_PWM_ISR.hpp @@ -0,0 +1,259 @@ +/**************************************************************************************************************************** + STM32_Slow_PWM_ISR.hpp + For STM32F/L/H/G/WB/MP1 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/STM32_Slow_PWM + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one STM32 timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Version: 1.2.3 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K.Hoang 22/09/2021 Initial coding for STM32F/L/H/G/WB/MP1 + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly + 1.2.0 K Hoang 29/01/2022 Fix multiple-definitions linker error. Improve accuracy + 1.2.1 K Hoang 30/01/2022 DutyCycle to be updated at the end current PWM period + 1.2.2 K Hoang 01/02/2022 Use float for DutyCycle and Freq, uint32_t for period. Optimize code + 1.2.3 K Hoang 03/03/2022 Fix `DutyCycle` and `New Period` display bugs. Display warning only when debug level > 3 +*****************************************************************************************************************************/ + +#pragma once + +#ifndef STM32_SLOW_PWM_ISR_HPP +#define STM32_SLOW_PWM_ISR_HPP + +#if defined(ARDUINO) + #if ARDUINO >= 100 + #include + #else + #include + #endif +#endif + +#if !( defined(STM32F0) || defined(STM32F1) || defined(STM32F2) || defined(STM32F3) ||defined(STM32F4) || defined(STM32F7) || \ + defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32H7) ||defined(STM32G0) || defined(STM32G4) || \ + defined(STM32WB) || defined(STM32MP1) || defined(STM32L5)) + #error This code is designed to run on STM32F/L/H/G/WB/MP1 platform! Please check your Tools->Board setting. +#endif + +#ifndef STM32_SLOW_PWM_VERSION + #define STM32_SLOW_PWM_VERSION "STM32_SLOW_PWM v1.2.3" + + #define STM32_SLOW_PWM_VERSION_MAJOR 1 + #define STM32_SLOW_PWM_VERSION_MINOR 2 + #define STM32_SLOW_PWM_VERSION_PATCH 3 + + #define STM32_SLOW_PWM_VERSION_INT 1002003 +#endif + +#ifndef _PWM_LOGLEVEL_ + #define _PWM_LOGLEVEL_ 1 +#endif + +#include "PWM_Generic_Debug.h" + +#include + +#include + +#define STM32_SLOW_PWM_ISR STM32_Slow_PWM + +typedef void (*timer_callback)(); +typedef void (*timer_callback_p)(void *); + +#if !defined(USING_MICROS_RESOLUTION) + + #if (_PWM_LOGLEVEL_ > 3) + #warning Not USING_MICROS_RESOLUTION, using millis resolution + #endif + + #define USING_MICROS_RESOLUTION false +#endif + +#if !defined(CHANGING_PWM_END_OF_CYCLE) + #if (_PWM_LOGLEVEL_ > 3) + #warning Using default CHANGING_PWM_END_OF_CYCLE == true + #endif + + #define CHANGING_PWM_END_OF_CYCLE true +#endif + +#define INVALID_STM32_PIN 255 + + +class STM32_SLOW_PWM_ISR +{ + + public: + // maximum number of PWM channels +#define MAX_NUMBER_CHANNELS 16 + + // constructor + STM32_SLOW_PWM_ISR(); + + void init(); + + // this function must be called inside loop() + void run(); + + ////////////////////////////////////////////////////////////////// + // PWM + // Return the channelNum if OK, -1 if error + int8_t setPWM(const uint32_t& pin, const float& frequency, const float& dutycycle, timer_callback StartCallback = nullptr, + timer_callback StopCallback = nullptr) + { + uint32_t period = 0; + + if ( ( frequency > 0.0 ) && ( frequency <= 1000.0 ) ) + { +#if USING_MICROS_RESOLUTION + // period in us + period = 1000000.0f / frequency; +#else + // period in ms + period = 1000.0f / frequency; +#endif + } + else + { + PWM_LOGERROR("Error: Invalid frequency, max is 1000Hz"); + + return -1; + } + + return setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); + } + + // period in us + // Return the channelNum if OK, -1 if error + int8_t setPWM_Period(const uint32_t& pin, const uint32_t& period, const float& dutycycle, + timer_callback StartCallback = nullptr, timer_callback StopCallback = nullptr) + { + return setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); + } + + ////////////////////////////////////////////////////////////////// + + // low level function to modify a PWM channel + // returns the true on success or false on failure + bool modifyPWMChannel(const uint8_t& channelNum, const uint32_t& pin, const float& frequency, const float& dutycycle) + { + uint32_t period = 0; + + if ( ( frequency > 0.0 ) && ( frequency <= 1000.0 ) ) + { +#if USING_MICROS_RESOLUTION + // period in us + period = 1000000.0f / frequency; +#else + // period in ms + period = 1000.0f / frequency; +#endif + } + else + { + PWM_LOGERROR("Error: Invalid frequency, max is 1000Hz"); + return false; + } + + return modifyPWMChannel_Period(channelNum, pin, period, dutycycle); + } + + ////////////////////////////////////////////////////////////////// + + //period in us + bool modifyPWMChannel_Period(const uint8_t& channelNum, const uint32_t& pin, const uint32_t& period, const float& dutycycle); + + ////////////////////////////////////////////////////////////////// + + // destroy the specified PWM channel + void deleteChannel(const uint8_t& channelNum); + + // restart the specified PWM channel + void restartChannel(const uint8_t& channelNum); + + // returns true if the specified PWM channel is enabled + bool isEnabled(const uint8_t& channelNum); + + // enables the specified PWM channel + void enable(const uint8_t& channelNum); + + // disables the specified PWM channel + void disable(const uint8_t& channelNum); + + // enables all PWM channels + void enableAll(); + + // disables all PWM channels + void disableAll(); + + // enables the specified PWM channel if it's currently disabled, and vice-versa + void toggle(const uint8_t& channelNum); + + // returns the number of used PWM channels + int8_t getnumChannels(); + + // returns the number of available PWM channels + uint8_t getNumAvailablePWMChannels() + { + if (numChannels <= 0) + return MAX_NUMBER_CHANNELS; + else + return MAX_NUMBER_CHANNELS - numChannels; + }; + + private: + + // low level function to initialize and enable a new PWM channel + // returns the PWM channel number (channelNum) on success or + // -1 on failure (f == NULL) or no free PWM channels + int8_t setupPWMChannel(const uint32_t& pin, const uint32_t& period, const float& dutycycle, void* cbStartFunc = nullptr, void* cbStopFunc = nullptr); + + // find the first available slot + int8_t findFirstFreeSlot(); + + typedef struct + { + /////////////////////////////////// + + + /////////////////////////////////// + + uint64_t prevTime; // value returned by the micros() or millis() function in the previous run() call + uint32_t period; // period value, in us / ms + uint32_t onTime; // onTime value, ( period * dutyCycle / 100 ) us / ms + + void* callbackStart; // pointer to the callback function when PWM pulse starts (HIGH) + void* callbackStop; // pointer to the callback function when PWM pulse stops (LOW) + + //////////////////////////////////////////////////////////// + + uint32_t pin; // PWM pin + bool pinHigh; // true if PWM pin is HIGH + //////////////////////////////////////////////////////////// + + bool enabled; // true if enabled + + // New from v1.2.1 + uint32_t newPeriod; // period value, in us / ms + uint32_t newOnTime; // onTime value, ( period * dutyCycle / 100 ) us / ms + float newDutyCycle; // from 0.00 to 100.00, float precision + ////// + } PWM_t; + + volatile PWM_t PWM[MAX_NUMBER_CHANNELS]; + + // actual number of PWM channels in use (-1 means uninitialized) + volatile int8_t numChannels; +}; + +#endif // STM32_SLOW_PWM_ISR_HPP + + diff --git a/src/STM32_Slow_PWM_ISR_Impl.h b/src/STM32_Slow_PWM_ISR_Impl.h new file mode 100644 index 0000000..2620201 --- /dev/null +++ b/src/STM32_Slow_PWM_ISR_Impl.h @@ -0,0 +1,386 @@ +/**************************************************************************************************************************** + STM32_Slow_PWM_ISR_Impl.h + For STM32F/L/H/G/WB/MP1 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/STM32_Slow_PWM + Licensed under MIT license + + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by + unsigned long miliseconds), you just consume only one STM32 timer and avoid conflicting with other cores' tasks. + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Version: 1.2.3 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K.Hoang 22/09/2021 Initial coding for STM32F/L/H/G/WB/MP1 + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly + 1.2.0 K Hoang 29/01/2022 Fix multiple-definitions linker error. Improve accuracy + 1.2.1 K Hoang 30/01/2022 DutyCycle to be updated at the end current PWM period + 1.2.2 K Hoang 01/02/2022 Use float for DutyCycle and Freq, uint32_t for period. Optimize code + 1.2.3 K Hoang 03/03/2022 Fix `DutyCycle` and `New Period` display bugs. Display warning only when debug level > 3 +*****************************************************************************************************************************/ + +#pragma once + +#ifndef STM32_SLOW_PWM_ISR_IMPL_H +#define STM32_SLOW_PWM_ISR_IMPL_H + +#include + +/////////////////////////////////////////////////// + + +uint64_t timeNow() +{ +#if USING_MICROS_RESOLUTION + return ( (uint64_t) micros() ); +#else + return ( (uint64_t) millis() ); +#endif +} + +/////////////////////////////////////////////////// + +STM32_SLOW_PWM_ISR::STM32_SLOW_PWM_ISR() + : numChannels (-1) +{ +} + +/////////////////////////////////////////////////// + +void STM32_SLOW_PWM_ISR::init() +{ + uint64_t currentTime = timeNow(); + + for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) + { + memset((void*) &PWM[channelNum], 0, sizeof (PWM_t)); + PWM[channelNum].prevTime = currentTime; + PWM[channelNum].pin = INVALID_STM32_PIN; + } + + numChannels = 0; +} + +/////////////////////////////////////////////////// + +void STM32_SLOW_PWM_ISR::run() +{ + uint64_t currentTime = timeNow(); + + for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) + { + // If enabled => check + // start period / dutyCycle => digitalWrite HIGH + // end dutyCycle => digitalWrite LOW + if (PWM[channelNum].enabled) + { + if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) <= PWM[channelNum].onTime ) + { + if (!PWM[channelNum].pinHigh) + { + digitalWrite(PWM[channelNum].pin, HIGH); + PWM[channelNum].pinHigh = true; + + // callbackStart + if (PWM[channelNum].callbackStart != nullptr) + { + (*(timer_callback) PWM[channelNum].callbackStart)(); + } + } + } + else if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) < PWM[channelNum].period ) + { + if (PWM[channelNum].pinHigh) + { + digitalWrite(PWM[channelNum].pin, LOW); + PWM[channelNum].pinHigh = false; + + // callback when PWM pulse stops (LOW) + if (PWM[channelNum].callbackStop != nullptr) + { + (*(timer_callback) PWM[channelNum].callbackStop)(); + } + } + } + //else + else if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) >= PWM[channelNum].period ) + { + PWM[channelNum].prevTime = currentTime; + +#if CHANGING_PWM_END_OF_CYCLE + // Only update whenever having newPeriod + if (PWM[channelNum].newPeriod != 0) + { + PWM[channelNum].period = PWM[channelNum].newPeriod; + PWM[channelNum].newPeriod = 0; + + PWM[channelNum].onTime = PWM[channelNum].newOnTime; + } +#endif + } + } + } +} + + +/////////////////////////////////////////////////// + +// find the first available slot +// return -1 if none found +int8_t STM32_SLOW_PWM_ISR::findFirstFreeSlot() +{ + // all slots are used + if (numChannels >= MAX_NUMBER_CHANNELS) + { + return -1; + } + + // return the first slot with no callbackStart (i.e. free) + for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) + { + if ( (PWM[channelNum].period == 0) && !PWM[channelNum].enabled ) + { + return channelNum; + } + } + + // no free slots found + return -1; +} + +/////////////////////////////////////////////////// + +int8_t STM32_SLOW_PWM_ISR::setupPWMChannel(const uint32_t& pin, const uint32_t& period, const float& dutycycle, void* cbStartFunc, void* cbStopFunc) +{ + int8_t channelNum; + + // Invalid input, such as period = 0, etc + if ( (period == 0) || (dutycycle < 0.0) || (dutycycle > 100.0) ) + { + PWM_LOGERROR("Error: Invalid period or dutycycle"); + return -1; + } + + if (numChannels < 0) + { + init(); + } + + channelNum = findFirstFreeSlot(); + + if (channelNum < 0) + { + return -1; + } + + PWM[channelNum].pin = pin; + PWM[channelNum].period = period; + + // Must be 0 for new PWM channel + PWM[channelNum].newPeriod = 0; + + PWM[channelNum].onTime = ( period * dutycycle ) / 100; + + pinMode(pin, OUTPUT); + digitalWrite(pin, HIGH); + PWM[channelNum].pinHigh = true; + + PWM[channelNum].prevTime = timeNow(); + + PWM[channelNum].callbackStart = cbStartFunc; + PWM[channelNum].callbackStop = cbStopFunc; + + PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum); + PWM_LOGINFO0("\t Period : "); PWM_LOGINFO0(PWM[channelNum].period); + PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(PWM[channelNum].onTime); + PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(PWM[channelNum].prevTime); + + numChannels++; + + PWM[channelNum].enabled = true; + + return channelNum; +} + +/////////////////////////////////////////////////// + +bool STM32_SLOW_PWM_ISR::modifyPWMChannel_Period(const uint8_t& channelNum, const uint32_t& pin, const uint32_t& period, const float& dutycycle) +{ + // Invalid input, such as period = 0, etc + if ( (period == 0) || (dutycycle < 0.0) || (dutycycle > 100.0) ) + { + PWM_LOGERROR("Error: Invalid period or dutycycle"); + return false; + } + + if (channelNum > MAX_NUMBER_CHANNELS) + { + PWM_LOGERROR("Error: channelNum > MAX_NUMBER_CHANNELS"); + return false; + } + + if (PWM[channelNum].pin != pin) + { + PWM_LOGERROR("Error: channelNum and pin mismatched"); + return false; + } + +#if CHANGING_PWM_END_OF_CYCLE + + PWM[channelNum].newPeriod = period; + PWM[channelNum].newDutyCycle = dutycycle; + PWM[channelNum].newOnTime = ( period * dutycycle ) / 100; + + PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum); + PWM_LOGINFO0("\t Period : "); PWM_LOGINFO0(period); + PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(PWM[channelNum].newOnTime); + PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(PWM[channelNum].prevTime); + +#else + + PWM[channelNum].period = period; + + PWM[channelNum].onTime = ( period * dutycycle ) / 100; + + digitalWrite(pin, HIGH); + PWM[channelNum].pinHigh = true; + + PWM[channelNum].prevTime = timeNow(); + + PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum); + PWM_LOGINFO0("\t Period : "); PWM_LOGINFO0(PWM[channelNum].period); + PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(PWM[channelNum].onTime); + PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(PWM[channelNum].prevTime); + +#endif + + return true; +} + +/////////////////////////////////////////////////// + + +void STM32_SLOW_PWM_ISR::deleteChannel(const uint8_t& channelNum) +{ + // nothing to delete if no timers are in use + if ( (channelNum >= MAX_NUMBER_CHANNELS) || (numChannels == 0) ) + { + return; + } + + // don't decrease the number of timers if the specified slot is already empty (zero period, invalid) + if ( (PWM[channelNum].pin != INVALID_STM32_PIN) && (PWM[channelNum].period != 0) ) + { + memset((void*) &PWM[channelNum], 0, sizeof (PWM_t)); + + PWM[channelNum].pin = INVALID_STM32_PIN; + + // update number of timers + numChannels--; + } +} + +/////////////////////////////////////////////////// + +// function contributed by code@rowansimms.com +void STM32_SLOW_PWM_ISR::restartChannel(const uint8_t& channelNum) +{ + if (channelNum >= MAX_NUMBER_CHANNELS) + { + return; + } + + PWM[channelNum].prevTime = timeNow(); +} + +/////////////////////////////////////////////////// + +bool STM32_SLOW_PWM_ISR::isEnabled(const uint8_t& channelNum) +{ + if (channelNum >= MAX_NUMBER_CHANNELS) + { + return false; + } + + return PWM[channelNum].enabled; +} + +/////////////////////////////////////////////////// + +void STM32_SLOW_PWM_ISR::enable(const uint8_t& channelNum) +{ + if (channelNum >= MAX_NUMBER_CHANNELS) + { + return; + } + + PWM[channelNum].enabled = true; +} + +/////////////////////////////////////////////////// + +void STM32_SLOW_PWM_ISR::disable(const uint8_t& channelNum) +{ + if (channelNum >= MAX_NUMBER_CHANNELS) + { + return; + } + + PWM[channelNum].enabled = false; +} + +/////////////////////////////////////////////////// + +void STM32_SLOW_PWM_ISR::enableAll() +{ + // Enable all timers with a callbackStart assigned (used) + + for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) + { + if (PWM[channelNum].period != 0) + { + PWM[channelNum].enabled = true; + } + } +} + +/////////////////////////////////////////////////// + +void STM32_SLOW_PWM_ISR::disableAll() +{ + // Disable all timers with a callbackStart assigned (used) + for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) + { + if (PWM[channelNum].period != 0) + { + PWM[channelNum].enabled = false; + } + } +} + +/////////////////////////////////////////////////// + +void STM32_SLOW_PWM_ISR::toggle(const uint8_t& channelNum) +{ + if (channelNum >= MAX_NUMBER_CHANNELS) + { + return; + } + + PWM[channelNum].enabled = !PWM[channelNum].enabled; +} + +/////////////////////////////////////////////////// + +int8_t STM32_SLOW_PWM_ISR::getnumChannels() +{ + return numChannels; +} + +#endif // STM32_SLOW_PWM_ISR_IMPL_H +