From 3e47dd6dddc8c1d1f9350a4e59458465b4502258 Mon Sep 17 00:00:00 2001 From: stephenhensley Date: Tue, 6 Dec 2022 16:36:38 -0800 Subject: [PATCH 01/11] added tim_channel class. --- Makefile | 5 +- src/daisy.h | 1 + src/per/tim_channel.cpp | 141 ++++++++++++++++++++++++++++++++++++++++ src/per/tim_channel.h | 75 +++++++++++++++++++++ 4 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 src/per/tim_channel.cpp create mode 100644 src/per/tim_channel.h diff --git a/Makefile b/Makefile index 812d0bcc6..8d915f4a2 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,7 @@ per/qspi \ per/spi \ per/spiMultislave \ per/tim \ +per/tim_channel \ per/uart \ ui/UI \ ui/AbstractMenu \ @@ -69,8 +70,8 @@ util/WaveTableLoader \ ###################################### # building variables ###################################### -DEBUG = 0 -OPT = -O3 +DEBUG = 1 +OPT = -O0 ####################################### # paths diff --git a/src/daisy.h b/src/daisy.h index 38023cce6..cff8ee5a2 100644 --- a/src/daisy.h +++ b/src/daisy.h @@ -34,6 +34,7 @@ #include "per/sdmmc.h" #include "per/spi.h" #include "per/spiMultislave.h" +#include "per/tim_channel.h" #include "per/rng.h" #include "hid/disp/display.h" #include "hid/disp/oled_display.h" diff --git a/src/per/tim_channel.cpp b/src/per/tim_channel.cpp new file mode 100644 index 000000000..5be455249 --- /dev/null +++ b/src/per/tim_channel.cpp @@ -0,0 +1,141 @@ +#include "tim_channel.h" +#include "util/hal_map.h" + +namespace daisy +{ + +/** Pin Mappings: + * TODO: Make a map + * + * TIM2 CH1 - PA0 (AF1), PA5 (AF1) + * TIM2 CH2 - PA1 (AF1), PB3 (AF1) + * TIM2 CH3 - PA2 (AF1), PB10 (AF1) + * TIM2 CH4 - PA3 (AF1), PB11 (AF1) + * TIM3 CH1 - PA6 (AF2), PB4 (AF2), PC6 (AF2) + * TIM3 CH2 - PA7 (AF2), PB5 (AF2), PC7 (AF2) + * TIM3 CH3 - PB0 (AF2), PC8 (AF2) + * TIM3 CH4 - PB1 (AF2), PC9 (AF2) + * TIM4 CH1 - PD12 (AF2), PB6 (AF2) + * TIM4 CH2 - PD13 (AF2), PB7 (AF2) + * TIM4 CH3 - PD14 (AF2), PB8 (AF2) + * TIM4 CH4 - PD15 (AF2), PB9 (AF2) + * TIM5 CH1 - PA0 (AF2), PH10 (AF2) + * TIM5 CH2 - PA1 (AF2), PH11 (AF2) + * TIM5 CH3 - PA2 (AF2), PH12 (AF2) + * TIM5 CH4 - PA3 (AF2), PI0 (AF2) + * + * And without a map: + * TIM2 ChN (AF1) + * TIM3 ChN (AF2) + * TIM4 ChN (AF2) + * TIM5 ChN (AF2) + */ + +/** Sets the instance of the HAL TIM Handle based on the values in the Daisy struct + * This also returns the AF value for a given timer (can be used for GPIO init). + */ +static uint32_t SetInstance(TIM_HandleTypeDef* tim, + TimerHandle::Config::Peripheral dsy_periph) +{ + /** If getting a new TIM_Handle from HAL is problematic + * we can add some (not-ideal) method of getting the HAL + * object out of the TIM implementation for channel config. + * + * However, I think this should be okay because the instance is already + * initialized + * + * We're also going to prepopulate the AF number here sincde it's + * linked to the peripheral. + * Based on map above, only TIM2 uses AF1, otherwise AF2 + */ + uint32_t af_value; + switch(dsy_periph) + { + case TimerHandle::Config::Peripheral::TIM_2: + tim->Instance = TIM2; + af_value = GPIO_AF1_TIM2; + break; + case TimerHandle::Config::Peripheral::TIM_3: + tim->Instance = TIM3; + af_value = GPIO_AF2_TIM3; + break; + case TimerHandle::Config::Peripheral::TIM_4: + tim->Instance = TIM4; + af_value = GPIO_AF2_TIM4; + break; + case TimerHandle::Config::Peripheral::TIM_5: + tim->Instance = TIM5; + af_value = GPIO_AF2_TIM5; + break; + } + return af_value; +} + +static uint32_t GetHalChannel(TimChannel::Config::Channel chn) +{ + auto hal_chn = chn == TimChannel::Config::Channel::ONE ? TIM_CHANNEL_1 + : chn == TimChannel::Config::Channel::TWO ? TIM_CHANNEL_2 + : chn == TimChannel::Config::Channel::THREE ? TIM_CHANNEL_3 + : chn == TimChannel::Config::Channel::FOUR ? TIM_CHANNEL_4 + : TIM_CHANNEL_1; + return hal_chn; +} + +void TimChannel::Init(const TimChannel::Config& cfg) +{ + cfg_ = cfg; + /** Configure Channel */ + TIM_OC_InitTypeDef sConfigOC = {0}; + sConfigOC.OCMode = TIM_OCMODE_PWM1; + sConfigOC.Pulse = 0; + sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; + sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; + auto chn = GetHalChannel(cfg.chn); + TIM_HandleTypeDef tim; + auto af_value = SetInstance(&tim, cfg.tim->GetConfig().periph); + HAL_TIM_PWM_ConfigChannel(&tim, &sConfigOC, chn); + + /** TODO: remove conversion to old pin, and add hal map for new Pin type */ + dsy_gpio_pin tpin = cfg.pin; + GPIO_TypeDef* port = dsy_hal_map_get_port(&tpin); + uint16_t pin = dsy_hal_map_get_pin(&tpin); + /** Start Clock for port (if necessary) */ + dsy_hal_map_gpio_clk_enable(tpin.port); + /** Intilize the actual pin */ + GPIO_InitTypeDef gpio_init = {0}; + gpio_init.Pin = pin; + gpio_init.Mode = GPIO_MODE_AF_PP; + gpio_init.Pull = GPIO_NOPULL; + gpio_init.Speed = GPIO_SPEED_MEDIUM; + gpio_init.Alternate = af_value; + HAL_GPIO_Init(port, &gpio_init); +} + +TimChannel::Config& TimChannel::GetConfig() +{ + return cfg_; +} + +void TimChannel::Start() +{ + TIM_HandleTypeDef tim; + SetInstance(&tim, cfg_.tim->GetConfig().periph); + HAL_TIM_PWM_Start(&tim, GetHalChannel(cfg_.chn)); +} +void TimChannel::Stop() +{ + TIM_HandleTypeDef tim; + SetInstance(&tim, cfg_.tim->GetConfig().periph); + HAL_TIM_PWM_Stop(&tim, GetHalChannel(cfg_.chn)); +} + +void TimChannel::SetPwm(uint32_t val) +{ + TIM_HandleTypeDef tim; + SetInstance(&tim, cfg_.tim->GetConfig().periph); + __HAL_TIM_SET_COMPARE(&tim, GetHalChannel(cfg_.chn), val); +} + +void TimChannel::StartDma(void* data, size_t size, void* callback) {} + +} // namespace daisy \ No newline at end of file diff --git a/src/per/tim_channel.h b/src/per/tim_channel.h new file mode 100644 index 000000000..486a7e816 --- /dev/null +++ b/src/per/tim_channel.h @@ -0,0 +1,75 @@ +#include "daisy_core.h" +#include "tim.h" + +/** Some notes as I set stuff up: + * + * The HAL_TIM_Base_Init() seems to cover everything we need, + * everything for the "channel" I/O happens via the MspInit HAL callbacks + * Since we'll be handling these post-init, on channel-by-channel option + * we can probably avoid doing the {function}Init stuff, and populating any MspInit + * + * Also, I'm including all of the modes here for now, but I'm really focused on + * implementing PWM (via DMA). So I will do all of the implementation for that, + * and then see what doing IC, OC, and ONEPULSE would look like. + * + * also we'll _probably_ have to do a pimpl, but we'll see what we can do without one for the moment. + * + */ + +namespace daisy +{ +class TimChannel +{ + public: + struct Config + { + /** Specifies the Channel to use */ + enum class Channel + { + ONE, + TWO, + THREE, + FOUR, + }; + + enum class Mode + { + INPUT_CAPTURE, + OUTPUT_COMPARE, + PWM_GENERATION, + ONE_PULSE, + }; + + TimerHandle* tim; + Channel chn; + Mode mode; + Pin pin; + + Config() : tim(nullptr), chn(Channel::ONE) {} + }; + + TimChannel() {} + ~TimChannel() {} + + /** Initializes the GPIO Pin and sets up PWM for the given channel */ + void Init(const Config& cfg); + + /** Starts the PWM output on the given channel's pin */ + void Start(); + + /** Stops the PWM output on the given channel's pin */ + void Stop(); + + void SetPwm(uint32_t val); + + /** Starts the DMA for the given buffer, calling the callback + * when the transmission is complete. + */ + void StartDma(void* data, size_t size, void* callback); + + Config& GetConfig(); + + private: + Config cfg_; +}; +} // namespace daisy \ No newline at end of file From f912c8f7b2cfcdb90d961d601934adb5aa0586e6 Mon Sep 17 00:00:00 2001 From: stephenhensley Date: Tue, 6 Dec 2022 16:36:56 -0800 Subject: [PATCH 02/11] missing extern in hal map files --- src/util/hal_map.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/util/hal_map.h b/src/util/hal_map.h index acb7d23e4..9d280198a 100644 --- a/src/util/hal_map.h +++ b/src/util/hal_map.h @@ -1,6 +1,9 @@ #pragma once #ifndef DSY_HAL_MAP_H #define DSY_HAL_MAP_H +#ifdef __cplusplus +extern "C" { +#endif #include "stm32h7xx_hal.h" #include "daisy_core.h" @@ -30,5 +33,9 @@ uint16_t dsy_hal_map_get_pin(const dsy_gpio_pin *p); */ void dsy_hal_map_gpio_clk_enable(dsy_gpio_port port); +#ifdef __cplusplus +} +#endif + #endif /** @} */ From f705e1a7b4dc52a45a8125e018a4d6a86dae0519 Mon Sep 17 00:00:00 2001 From: stephenhensley Date: Tue, 6 Dec 2022 16:38:26 -0800 Subject: [PATCH 03/11] pwm example and updated launch.json to test it --- .vscode/launch.json | 31 ++++++++++++++ examples/PWM/Basic-PWM/Makefile | 12 ++++++ examples/PWM/Basic-PWM/basic-pwm.cpp | 60 ++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 examples/PWM/Basic-PWM/Makefile create mode 100644 examples/PWM/Basic-PWM/basic-pwm.cpp diff --git a/.vscode/launch.json b/.vscode/launch.json index 5b38355cb..18e311719 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -21,6 +21,37 @@ "windows": { "MIMode": "gdb", } + }, + { + "name": "Debug", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32h7x.cfg" + ], + "cwd": "${workspaceFolder}", + "debuggerArgs": [ + "-d", + "${workspaceRoot}" + ], + // Here's where you can put the path to the program you want to debug: + "executable": "${workspaceRoot}/examples/PWM/Basic-PWM/build/basic-pwm.elf", + "interface": "swd", + "openOCDLaunchCommands": [ + "init", + "reset init", + "gdb_breakpoint_override hard" + ], + "preRestartCommands": [ + "load", + "enable breakpoint", + "monitor reset" + ], + "request": "launch", + "runToMain": true, + "servertype": "openocd", + "showDevDebugOutput": true, + "svdFile": "${workspaceRoot}/.vscode/STM32H750x.svd", + "type": "cortex-debug" } ] } \ No newline at end of file diff --git a/examples/PWM/Basic-PWM/Makefile b/examples/PWM/Basic-PWM/Makefile new file mode 100644 index 000000000..e732b4f78 --- /dev/null +++ b/examples/PWM/Basic-PWM/Makefile @@ -0,0 +1,12 @@ +# Project Name +TARGET = basic-pwm + +# Sources +CPP_SOURCES = basic-pwm.cpp + +# Library Locations +LIBDAISY_DIR = ../../.. + +# Core location, and generic Makefile. +SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core +include $(SYSTEM_FILES_DIR)/Makefile diff --git a/examples/PWM/Basic-PWM/basic-pwm.cpp b/examples/PWM/Basic-PWM/basic-pwm.cpp new file mode 100644 index 000000000..f50f81731 --- /dev/null +++ b/examples/PWM/Basic-PWM/basic-pwm.cpp @@ -0,0 +1,60 @@ +/** Basic PWM + * + * In this example, we will configure a single GPIO + * to output PWM using the TIM hardware built into the daisy. + * + * For this we're going to use Daisy Seed pin D17 as TIM3 Channel 4 + */ +#include "daisy_seed.h" + +using namespace daisy; + +/** Hardware object for communicating with Daisy */ +DaisySeed hw; +TimerHandle tim; + +int main(void) +{ + /** Initialize hardware */ + hw.Init(); + + /** Set up TIM for PWM + * 800kHz clock rate, w/ 8-bit resolution + * = about 3.125kHz refresh rate + * + * Typical uses could work with much slower timers, or higher resolution + */ + TimerHandle::Config tim_cfg; + tim_cfg.periph = TimerHandle::Config::Peripheral::TIM_3; + tim_cfg.period = 65536; /**< Not correct for 800kHz */ + tim.Init(tim_cfg); + tim.SetPeriod(65536); + tim.SetPrescaler(1); + + TimChannel::Config chn_cfg; + chn_cfg.tim = &tim; + chn_cfg.chn = TimChannel::Config::Channel::THREE; + chn_cfg.mode = TimChannel::Config::Mode::PWM_GENERATION; + chn_cfg.pin = seed::D17; + TimChannel pwm; + /** Initialize PWM */ + pwm.Init(chn_cfg); + tim.SetPeriod(65536); + tim.SetPrescaler(1); + tim.Start(); + pwm.Start(); + + /** Step through some brightness values */ + int vals[8]; + for(int i = 0; i < 8; i++) + vals[i] = i * (tim_cfg.period / 8); + + while(1) + { + for(int i = 0; i < 8; i++) + { + pwm.SetPwm(vals[i]); + System::Delay(250); + } + } +} From 07340c91092a9259d85a6b0312ef3a410990d921 Mon Sep 17 00:00:00 2001 From: stephenhensley Date: Tue, 6 Dec 2022 23:13:33 -0800 Subject: [PATCH 04/11] cleaned up, and fixed example to work with D17 --- examples/PWM/Basic-PWM/Makefile | 3 +++ examples/PWM/Basic-PWM/basic-pwm.cpp | 32 +++++++++++----------------- src/per/tim_channel.h | 2 +- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/examples/PWM/Basic-PWM/Makefile b/examples/PWM/Basic-PWM/Makefile index e732b4f78..760c2f75d 100644 --- a/examples/PWM/Basic-PWM/Makefile +++ b/examples/PWM/Basic-PWM/Makefile @@ -4,6 +4,9 @@ TARGET = basic-pwm # Sources CPP_SOURCES = basic-pwm.cpp +DEBUG=1 +OPT=-O0 + # Library Locations LIBDAISY_DIR = ../../.. diff --git a/examples/PWM/Basic-PWM/basic-pwm.cpp b/examples/PWM/Basic-PWM/basic-pwm.cpp index f50f81731..9641e2487 100644 --- a/examples/PWM/Basic-PWM/basic-pwm.cpp +++ b/examples/PWM/Basic-PWM/basic-pwm.cpp @@ -10,38 +10,32 @@ using namespace daisy; /** Hardware object for communicating with Daisy */ -DaisySeed hw; -TimerHandle tim; +DaisySeed hw; int main(void) { /** Initialize hardware */ hw.Init(); - /** Set up TIM for PWM - * 800kHz clock rate, w/ 8-bit resolution - * = about 3.125kHz refresh rate - * - * Typical uses could work with much slower timers, or higher resolution - */ + /** Configure frequency (800kHz) */ + auto tim_target_freq = 800000; + auto tim_base_freq = System::GetPClk2Freq(); + TimerHandle::Config tim_cfg; - tim_cfg.periph = TimerHandle::Config::Peripheral::TIM_3; - tim_cfg.period = 65536; /**< Not correct for 800kHz */ - tim.Init(tim_cfg); - tim.SetPeriod(65536); - tim.SetPrescaler(1); + TimerHandle timer; + tim_cfg.periph = TimerHandle::Config::Peripheral::TIM_3; + tim_cfg.period = tim_base_freq / tim_target_freq; + timer.Init(tim_cfg); TimChannel::Config chn_cfg; - chn_cfg.tim = &tim; - chn_cfg.chn = TimChannel::Config::Channel::THREE; - chn_cfg.mode = TimChannel::Config::Mode::PWM_GENERATION; + chn_cfg.tim = &timer; + chn_cfg.chn = TimChannel::Config::Channel::FOUR; + chn_cfg.mode = TimChannel::Config::Mode::PWM; chn_cfg.pin = seed::D17; TimChannel pwm; /** Initialize PWM */ pwm.Init(chn_cfg); - tim.SetPeriod(65536); - tim.SetPrescaler(1); - tim.Start(); + timer.Start(); pwm.Start(); /** Step through some brightness values */ diff --git a/src/per/tim_channel.h b/src/per/tim_channel.h index 486a7e816..8044c1697 100644 --- a/src/per/tim_channel.h +++ b/src/per/tim_channel.h @@ -36,7 +36,7 @@ class TimChannel { INPUT_CAPTURE, OUTPUT_COMPARE, - PWM_GENERATION, + PWM, ONE_PULSE, }; From c055da77193201c431d6db33c9c9be7cd3755cb6 Mon Sep 17 00:00:00 2001 From: stephenhensley Date: Tue, 6 Dec 2022 23:23:54 -0800 Subject: [PATCH 05/11] smoothed up the example a little bit --- Makefile | 4 ++-- examples/PWM/Basic-PWM/basic-pwm.cpp | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 8d915f4a2..84ecc0c06 100644 --- a/Makefile +++ b/Makefile @@ -70,8 +70,8 @@ util/WaveTableLoader \ ###################################### # building variables ###################################### -DEBUG = 1 -OPT = -O0 +DEBUG = 0 +OPT = -O3 ####################################### # paths diff --git a/examples/PWM/Basic-PWM/basic-pwm.cpp b/examples/PWM/Basic-PWM/basic-pwm.cpp index 9641e2487..84fb865be 100644 --- a/examples/PWM/Basic-PWM/basic-pwm.cpp +++ b/examples/PWM/Basic-PWM/basic-pwm.cpp @@ -17,8 +17,8 @@ int main(void) /** Initialize hardware */ hw.Init(); - /** Configure frequency (800kHz) */ - auto tim_target_freq = 800000; + /** Configure frequency (12kHz) */ + auto tim_target_freq = 12000; auto tim_base_freq = System::GetPClk2Freq(); TimerHandle::Config tim_cfg; @@ -39,16 +39,17 @@ int main(void) pwm.Start(); /** Step through some brightness values */ - int vals[8]; - for(int i = 0; i < 8; i++) - vals[i] = i * (tim_cfg.period / 8); + int vals[32]; + for(int i = 0; i < 32; i++) + vals[i] = i * (tim_cfg.period / 32); while(1) { - for(int i = 0; i < 8; i++) + /* ~30Hz animation of our little 32-frame ramp wave. */ + for(int i = 0; i < 32; i++) { pwm.SetPwm(vals[i]); - System::Delay(250); + System::Delay(33); } } } From cf03fb85a0a4f9f654327af2f696b707a5b5bd88 Mon Sep 17 00:00:00 2001 From: stephenhensley Date: Wed, 7 Dec 2022 16:27:26 -0800 Subject: [PATCH 06/11] really hacking DMA in there for now as proof of concept.. needs severe refactor. --- .vscode/launch.json | 2 +- Makefile | 4 ++-- src/per/tim.h | 2 +- src/per/tim_channel.cpp | 50 ++++++++++++++++++++++++++++++----------- src/per/tim_channel.h | 5 ++--- src/sys/dma.c | 4 ++++ 6 files changed, 47 insertions(+), 20 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 18e311719..240330c1e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -34,7 +34,7 @@ "${workspaceRoot}" ], // Here's where you can put the path to the program you want to debug: - "executable": "${workspaceRoot}/examples/PWM/Basic-PWM/build/basic-pwm.elf", + "executable": "${workspaceRoot}/examples/PWM/DMA-PWM/build/dma-pwm.elf", "interface": "swd", "openOCDLaunchCommands": [ "init", diff --git a/Makefile b/Makefile index 84ecc0c06..8d915f4a2 100644 --- a/Makefile +++ b/Makefile @@ -70,8 +70,8 @@ util/WaveTableLoader \ ###################################### # building variables ###################################### -DEBUG = 0 -OPT = -O3 +DEBUG = 1 +OPT = -O0 ####################################### # paths diff --git a/src/per/tim.h b/src/per/tim.h index c37b25e0c..388b45c56 100644 --- a/src/per/tim.h +++ b/src/per/tim.h @@ -123,7 +123,7 @@ class TimerHandle ** This will adjust the rate of ticks: ** Calculated as APBN_Freq / prescalar per tick ** where APBN is APB1 for Most general purpose timers, - ** and APB2 for HRTIM,a nd the advanced timers. + ** and APB2 for HRTIM, and the advanced timers. ** This can be changed "on-the-fly" ** */ Result SetPrescaler(uint32_t val); diff --git a/src/per/tim_channel.cpp b/src/per/tim_channel.cpp index 5be455249..94add1e03 100644 --- a/src/per/tim_channel.cpp +++ b/src/per/tim_channel.cpp @@ -3,6 +3,7 @@ namespace daisy { +static DMA_HandleTypeDef timhdma; /** Pin Mappings: * TODO: Make a map @@ -37,17 +38,6 @@ namespace daisy static uint32_t SetInstance(TIM_HandleTypeDef* tim, TimerHandle::Config::Peripheral dsy_periph) { - /** If getting a new TIM_Handle from HAL is problematic - * we can add some (not-ideal) method of getting the HAL - * object out of the TIM implementation for channel config. - * - * However, I think this should be okay because the instance is already - * initialized - * - * We're also going to prepopulate the AF number here sincde it's - * linked to the peripheral. - * Based on map above, only TIM2 uses AF1, otherwise AF2 - */ uint32_t af_value; switch(dsy_periph) { @@ -111,7 +101,7 @@ void TimChannel::Init(const TimChannel::Config& cfg) HAL_GPIO_Init(port, &gpio_init); } -TimChannel::Config& TimChannel::GetConfig() +const TimChannel::Config& TimChannel::GetConfig() const { return cfg_; } @@ -135,7 +125,41 @@ void TimChannel::SetPwm(uint32_t val) SetInstance(&tim, cfg_.tim->GetConfig().periph); __HAL_TIM_SET_COMPARE(&tim, GetHalChannel(cfg_.chn), val); } +static TIM_HandleTypeDef globaltim; + +void TimChannel::StartDma(void* data, size_t size, void* callback) +{ + timhdma.Instance = DMA2_Stream5; + timhdma.Init.Request = DMA_REQUEST_TIM3_CH4; + timhdma.Init.Direction = DMA_MEMORY_TO_PERIPH; + + timhdma.Init.PeriphInc = DMA_PINC_DISABLE; + timhdma.Init.MemInc = DMA_MINC_ENABLE; + timhdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; + timhdma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; + timhdma.Init.Mode = DMA_NORMAL; + timhdma.Init.Priority = DMA_PRIORITY_LOW; + timhdma.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + timhdma.Init.MemBurst = DMA_MBURST_SINGLE; + timhdma.Init.PeriphBurst = DMA_PBURST_SINGLE; + + if(HAL_DMA_Init(&timhdma) != HAL_OK) + { + // something bad + } + SetInstance(&globaltim, cfg_.tim->GetConfig().periph); + __HAL_LINKDMA(&globaltim, hdma[TIM_DMA_ID_CC4], timhdma); + HAL_TIM_PWM_Start_DMA(&globaltim, GetHalChannel(cfg_.chn), (uint32_t*)data, size); +} + + +extern "C" void DMA2_Stream5_IRQHandler(void) +{ + // DMA_HandleTypeDef timhdma; + // timhdma.Instance = DMA2_Stream5; + HAL_DMA_IRQHandler(&timhdma); +} -void TimChannel::StartDma(void* data, size_t size, void* callback) {} +extern "C" void DMAMUX1_OVR_IRQHandler(void) {} } // namespace daisy \ No newline at end of file diff --git a/src/per/tim_channel.h b/src/per/tim_channel.h index 8044c1697..b8a4e7c4a 100644 --- a/src/per/tim_channel.h +++ b/src/per/tim_channel.h @@ -44,8 +44,7 @@ class TimChannel Channel chn; Mode mode; Pin pin; - - Config() : tim(nullptr), chn(Channel::ONE) {} + Config() : tim(nullptr), chn(Channel::ONE), mode(Mode::PWM) {} }; TimChannel() {} @@ -67,7 +66,7 @@ class TimChannel */ void StartDma(void* data, size_t size, void* callback); - Config& GetConfig(); + const Config& GetConfig() const; private: Config cfg_; diff --git a/src/sys/dma.c b/src/sys/dma.c index 73c0a1ed7..78a28a393 100644 --- a/src/sys/dma.c +++ b/src/sys/dma.c @@ -48,6 +48,10 @@ extern "C" HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn); HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); + + // DMA2_Stream5 used for TIM Channel operation + HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn); } void dsy_dma_deinit(void) From 127f838ae2227bc7c0a4a13c0c69a82573a7daa1 Mon Sep 17 00:00:00 2001 From: stephenhensley Date: Wed, 7 Dec 2022 16:30:55 -0800 Subject: [PATCH 07/11] dma example workingg, and a copy to proof of concept digital LED stuff. --- .gitignore | 2 +- .vscode/launch.json | 2 +- examples/PWM/AddressableLED/Makefile | 17 +++++ .../PWM/AddressableLED/addressable-led.cpp | 69 +++++++++++++++++++ examples/PWM/DMA-PWM/Makefile | 17 +++++ examples/PWM/DMA-PWM/dma-pwm.cpp | 69 +++++++++++++++++++ 6 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 examples/PWM/AddressableLED/Makefile create mode 100644 examples/PWM/AddressableLED/addressable-led.cpp create mode 100644 examples/PWM/DMA-PWM/Makefile create mode 100644 examples/PWM/DMA-PWM/dma-pwm.cpp diff --git a/.gitignore b/.gitignore index ee4cfb75b..e0386f705 100644 --- a/.gitignore +++ b/.gitignore @@ -130,5 +130,5 @@ vs/*.log tests/libDaisy_gtest tests/build/bin/ -examples/*/build/ +examples/**/build/ diff --git a/.vscode/launch.json b/.vscode/launch.json index 240330c1e..bc98c51de 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -34,7 +34,7 @@ "${workspaceRoot}" ], // Here's where you can put the path to the program you want to debug: - "executable": "${workspaceRoot}/examples/PWM/DMA-PWM/build/dma-pwm.elf", + "executable": "${workspaceRoot}/examples/PWM/AddressableLED/build/addressable-led.elf", "interface": "swd", "openOCDLaunchCommands": [ "init", diff --git a/examples/PWM/AddressableLED/Makefile b/examples/PWM/AddressableLED/Makefile new file mode 100644 index 000000000..6b0c64748 --- /dev/null +++ b/examples/PWM/AddressableLED/Makefile @@ -0,0 +1,17 @@ +# Project Name +TARGET = addressable-led + +# Sources +CPP_SOURCES = addressable-led.cpp + +DEBUG=1 +OPT=-O0 + +C_DEFS += -DDEBUG_DEFAULT_INTERRUPT_HANDLERS + +# Library Locations +LIBDAISY_DIR = ../../.. + +# Core location, and generic Makefile. +SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core +include $(SYSTEM_FILES_DIR)/Makefile diff --git a/examples/PWM/AddressableLED/addressable-led.cpp b/examples/PWM/AddressableLED/addressable-led.cpp new file mode 100644 index 000000000..6c2cf3d5f --- /dev/null +++ b/examples/PWM/AddressableLED/addressable-led.cpp @@ -0,0 +1,69 @@ +/** DMA PWM + * + * In this example we will use the DMA to generate a PWM sequence + * This can be common for things like Digital LEDs (WS8212), motor control, etc. + * + * For this we're going to use Daisy Seed pin D17 as TIM3 Channel 4 (unless it has issues because of the LED attached..) + */ +#include "daisy_seed.h" + +using namespace daisy; + +/** Hardware object for communicating with Daisy */ +DaisySeed hw; +const size_t kOutBufferSize = 512; +uint32_t DMA_BUFFER_MEM_SECTION outbuffer[kOutBufferSize]; + +int main(void) +{ + /** Initialize hardware */ + hw.Init(); + + /** Initialize timer */ + TimerHandle::Config tim_cfg; + TimerHandle timer; + tim_cfg.periph = TimerHandle::Config::Peripheral::TIM_3; + timer.Init(tim_cfg); + + /** Generate period for timer + * This is a marvelously useful little tidbit that should be put into a TimerHandle function or something. + */ + uint32_t prescaler = 8; + uint32_t tickspeed = (System::GetPClk2Freq() * 2) / prescaler; + uint32_t target_pulse_freq = 833333; /**< 1.2 microsecond symbol length */ + uint32_t period = (tickspeed / target_pulse_freq) - 1; + timer.SetPrescaler(prescaler - 1); /**< ps=0 is divide by 1 and so on.*/ + timer.SetPeriod(period); + + TimChannel::Config chn_cfg; + chn_cfg.tim = &timer; + chn_cfg.chn = TimChannel::Config::Channel::FOUR; + chn_cfg.mode = TimChannel::Config::Mode::PWM; + chn_cfg.pin = seed::D17; + TimChannel pwm; + /** Fill Buffer */ + for(size_t i = 0; i < kOutBufferSize; i++) + { + float t = (float)i / (float)(kOutBufferSize - 1); /**< 0.0->1.0 */ + float ts = 0.5f + (cos(t * 6.28) * 0.5f); + outbuffer[i] = (uint32_t)(ts * period); + } + /** Initialize PWM */ + pwm.Init(chn_cfg); + timer.Start(); + pwm.Start(); + System::Delay(1000); + pwm.StartDma(outbuffer, kOutBufferSize, nullptr); + bool led_state = true; + + while(1) + { + System::Delay(2000); + hw.SetLed(led_state); + if(led_state) + led_state = false; + else + led_state = true; + pwm.StartDma(outbuffer, kOutBufferSize, nullptr); + } +} diff --git a/examples/PWM/DMA-PWM/Makefile b/examples/PWM/DMA-PWM/Makefile new file mode 100644 index 000000000..49e593b6a --- /dev/null +++ b/examples/PWM/DMA-PWM/Makefile @@ -0,0 +1,17 @@ +# Project Name +TARGET = dma-pwm + +# Sources +CPP_SOURCES = dma-pwm.cpp + +DEBUG=1 +OPT=-O0 + +C_DEFS += -DDEBUG_DEFAULT_INTERRUPT_HANDLERS + +# Library Locations +LIBDAISY_DIR = ../../.. + +# Core location, and generic Makefile. +SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core +include $(SYSTEM_FILES_DIR)/Makefile diff --git a/examples/PWM/DMA-PWM/dma-pwm.cpp b/examples/PWM/DMA-PWM/dma-pwm.cpp new file mode 100644 index 000000000..6c2cf3d5f --- /dev/null +++ b/examples/PWM/DMA-PWM/dma-pwm.cpp @@ -0,0 +1,69 @@ +/** DMA PWM + * + * In this example we will use the DMA to generate a PWM sequence + * This can be common for things like Digital LEDs (WS8212), motor control, etc. + * + * For this we're going to use Daisy Seed pin D17 as TIM3 Channel 4 (unless it has issues because of the LED attached..) + */ +#include "daisy_seed.h" + +using namespace daisy; + +/** Hardware object for communicating with Daisy */ +DaisySeed hw; +const size_t kOutBufferSize = 512; +uint32_t DMA_BUFFER_MEM_SECTION outbuffer[kOutBufferSize]; + +int main(void) +{ + /** Initialize hardware */ + hw.Init(); + + /** Initialize timer */ + TimerHandle::Config tim_cfg; + TimerHandle timer; + tim_cfg.periph = TimerHandle::Config::Peripheral::TIM_3; + timer.Init(tim_cfg); + + /** Generate period for timer + * This is a marvelously useful little tidbit that should be put into a TimerHandle function or something. + */ + uint32_t prescaler = 8; + uint32_t tickspeed = (System::GetPClk2Freq() * 2) / prescaler; + uint32_t target_pulse_freq = 833333; /**< 1.2 microsecond symbol length */ + uint32_t period = (tickspeed / target_pulse_freq) - 1; + timer.SetPrescaler(prescaler - 1); /**< ps=0 is divide by 1 and so on.*/ + timer.SetPeriod(period); + + TimChannel::Config chn_cfg; + chn_cfg.tim = &timer; + chn_cfg.chn = TimChannel::Config::Channel::FOUR; + chn_cfg.mode = TimChannel::Config::Mode::PWM; + chn_cfg.pin = seed::D17; + TimChannel pwm; + /** Fill Buffer */ + for(size_t i = 0; i < kOutBufferSize; i++) + { + float t = (float)i / (float)(kOutBufferSize - 1); /**< 0.0->1.0 */ + float ts = 0.5f + (cos(t * 6.28) * 0.5f); + outbuffer[i] = (uint32_t)(ts * period); + } + /** Initialize PWM */ + pwm.Init(chn_cfg); + timer.Start(); + pwm.Start(); + System::Delay(1000); + pwm.StartDma(outbuffer, kOutBufferSize, nullptr); + bool led_state = true; + + while(1) + { + System::Delay(2000); + hw.SetLed(led_state); + if(led_state) + led_state = false; + else + led_state = true; + pwm.StartDma(outbuffer, kOutBufferSize, nullptr); + } +} From b255c86e71bf5ef5eb43d3c14f1ae28da03c6646 Mon Sep 17 00:00:00 2001 From: stephenhensley Date: Wed, 7 Dec 2022 17:03:03 -0800 Subject: [PATCH 08/11] started putting together some stuff to drive the LED data via pulsewidth. --- .../PWM/AddressableLED/addressable-led.cpp | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/examples/PWM/AddressableLED/addressable-led.cpp b/examples/PWM/AddressableLED/addressable-led.cpp index 6c2cf3d5f..8c146e895 100644 --- a/examples/PWM/AddressableLED/addressable-led.cpp +++ b/examples/PWM/AddressableLED/addressable-led.cpp @@ -4,6 +4,10 @@ * This can be common for things like Digital LEDs (WS8212), motor control, etc. * * For this we're going to use Daisy Seed pin D17 as TIM3 Channel 4 (unless it has issues because of the LED attached..) + * + * TODO: This won't work until this stuff is resolved: + * * Hardcoding for TIM3Ch4 is in the libDaisy stuff. Needs to get updated so we can use TIM4 Ch1 for the devboard + * * Some sort of stop/reset between transactions to prep LEDs for next signal */ #include "daisy_seed.h" @@ -14,6 +18,54 @@ DaisySeed hw; const size_t kOutBufferSize = 512; uint32_t DMA_BUFFER_MEM_SECTION outbuffer[kOutBufferSize]; +const int kOneTime = 2; +const int kZeroTime = 8; + +const int kNumLeds = 12; +uint8_t led_data[kNumLeds][3]; /**< RGB data */ +uint32_t DMA_BUFFER_MEM_SECTION + output_data[kNumLeds * 3 + * 8]; /**< PWM lengths data, one "duration" per bit */ + +/** buff expects that 8 elements are available for the one 8-bit color val*/ +static void populate_bits(uint8_t color_val, uint32_t *buff) +{ + for(int i = 0; i < 8; i++) + { + buff[i] = (color_val & i) > 0 ? kOneTime : kZeroTime; + } +} + +static void fill_led_data() +{ + /** TODO fix these to be accurate for necessary timing */ + const uint32_t one_time = 2; + const uint32_t zero_time = 10; + for(int i = 0; i < kNumLeds; i++) + { + /** Grab G, R, B for filling bytes */ + uint8_t g = led_data[i][1]; + uint8_t r = led_data[i][0]; + uint8_t b = led_data[i][2]; + auto data_index = i * 3 * 8; + populate_bits(g, &output_data[data_index]); + populate_bits(r, &output_data[data_index + 8]); + populate_bits(b, &output_data[data_index + 16]); + } +} + +static void set_led(int index, uint8_t r, uint8_t g, uint8_t b) +{ + led_data[index][0] = r; + led_data[index][1] = g; + led_data[index][2] = b; +} + +static void set_led_f(int index, float r, float g, float b) +{ + set_led(index, r * 255, g * 255, b * 255); +} + int main(void) { /** Initialize hardware */ @@ -22,7 +74,7 @@ int main(void) /** Initialize timer */ TimerHandle::Config tim_cfg; TimerHandle timer; - tim_cfg.periph = TimerHandle::Config::Peripheral::TIM_3; + tim_cfg.periph = TimerHandle::Config::Peripheral::TIM_4; timer.Init(tim_cfg); /** Generate period for timer @@ -37,9 +89,9 @@ int main(void) TimChannel::Config chn_cfg; chn_cfg.tim = &timer; - chn_cfg.chn = TimChannel::Config::Channel::FOUR; + chn_cfg.chn = TimChannel::Config::Channel::ONE; chn_cfg.mode = TimChannel::Config::Mode::PWM; - chn_cfg.pin = seed::D17; + chn_cfg.pin = seed::D13; TimChannel pwm; /** Fill Buffer */ for(size_t i = 0; i < kOutBufferSize; i++) From 7fc733b763814d862cdcd823369e4555b3fb77ca Mon Sep 17 00:00:00 2001 From: stephenhensley Date: Thu, 8 Dec 2022 08:54:28 -0800 Subject: [PATCH 09/11] temp. added some switch cases for DMA request, etc to resolve hard coded tim/channel, and got example ready to test. --- .../PWM/AddressableLED/addressable-led.cpp | 58 ++++++++--- src/per/tim_channel.cpp | 99 ++++++++++++++++++- 2 files changed, 138 insertions(+), 19 deletions(-) diff --git a/examples/PWM/AddressableLED/addressable-led.cpp b/examples/PWM/AddressableLED/addressable-led.cpp index 8c146e895..11ae46130 100644 --- a/examples/PWM/AddressableLED/addressable-led.cpp +++ b/examples/PWM/AddressableLED/addressable-led.cpp @@ -18,11 +18,13 @@ DaisySeed hw; const size_t kOutBufferSize = 512; uint32_t DMA_BUFFER_MEM_SECTION outbuffer[kOutBufferSize]; -const int kOneTime = 2; +const int kOneTime = 20; const int kZeroTime = 8; const int kNumLeds = 12; uint8_t led_data[kNumLeds][3]; /**< RGB data */ + +const size_t kOutDataSize = kNumLeds * 3 * 8; uint32_t DMA_BUFFER_MEM_SECTION output_data[kNumLeds * 3 * 8]; /**< PWM lengths data, one "duration" per bit */ @@ -39,8 +41,6 @@ static void populate_bits(uint8_t color_val, uint32_t *buff) static void fill_led_data() { /** TODO fix these to be accurate for necessary timing */ - const uint32_t one_time = 2; - const uint32_t zero_time = 10; for(int i = 0; i < kNumLeds; i++) { /** Grab G, R, B for filling bytes */ @@ -103,19 +103,49 @@ int main(void) /** Initialize PWM */ pwm.Init(chn_cfg); timer.Start(); - pwm.Start(); - System::Delay(1000); - pwm.StartDma(outbuffer, kOutBufferSize, nullptr); - bool led_state = true; + //pwm.Start(); + // System::Delay(1000); + // pwm.StartDma(outbuffer, kOutBufferSize, nullptr); + // bool led_state = true; + + uint32_t now, tled; + now = tled = System::GetNow(); while(1) { - System::Delay(2000); - hw.SetLed(led_state); - if(led_state) - led_state = false; - else - led_state = true; - pwm.StartDma(outbuffer, kOutBufferSize, nullptr); + // System::Delay(2000); + // hw.SetLed(led_state); + // if(led_state) + // led_state = false; + // else + // led_state = true; + // pwm.StartDma(outbuffer, kOutBufferSize, nullptr); + + now = System::GetNow(); + if(now - tled > 33) + { + tled = now; + + /* Lets set some LED stuff */ + for(int i = 0; i < kNumLeds; i++) + { + float bright = (float)(now & 1023) / 1023.f; + set_led_f(i, bright, bright, bright); + } + fill_led_data(); + + /** And transmit */ + pwm.Start(); + pwm.StartDma(output_data, kOutDataSize, nullptr); + /** when its done..... + * we need to set pwm to 0% or pull low for >=80us + * this is hacky, gross, and would be the perfect thing to setup in the callback... + * + * It should _never_ take more than 1ms nevermind 3.. + * You'd need at least 36 LEDs for it to be 1ms of transmission. + */ + System::Delay(3); + pwm.Stop(); + } } } diff --git a/src/per/tim_channel.cpp b/src/per/tim_channel.cpp index 94add1e03..1470d54ea 100644 --- a/src/per/tim_channel.cpp +++ b/src/per/tim_channel.cpp @@ -129,8 +129,83 @@ static TIM_HandleTypeDef globaltim; void TimChannel::StartDma(void* data, size_t size, void* callback) { - timhdma.Instance = DMA2_Stream5; - timhdma.Init.Request = DMA_REQUEST_TIM3_CH4; + timhdma.Instance = DMA2_Stream5; + + + /** kind of nuts to set this.... */ + switch(cfg_.tim->GetConfig().periph) + { + case TimerHandle::Config::Peripheral::TIM_2: + switch(cfg_.chn) + { + case TimChannel::Config::Channel::ONE: + timhdma.Init.Request = DMA_REQUEST_TIM2_CH1; + break; + case TimChannel::Config::Channel::TWO: + timhdma.Init.Request = DMA_REQUEST_TIM2_CH2; + break; + case TimChannel::Config::Channel::THREE: + timhdma.Init.Request = DMA_REQUEST_TIM2_CH3; + break; + case TimChannel::Config::Channel::FOUR: + timhdma.Init.Request = DMA_REQUEST_TIM2_CH4; + break; + } + break; + case TimerHandle::Config::Peripheral::TIM_3: + switch(cfg_.chn) + { + case TimChannel::Config::Channel::ONE: + timhdma.Init.Request = DMA_REQUEST_TIM3_CH1; + break; + case TimChannel::Config::Channel::TWO: + timhdma.Init.Request = DMA_REQUEST_TIM3_CH2; + break; + case TimChannel::Config::Channel::THREE: + timhdma.Init.Request = DMA_REQUEST_TIM3_CH3; + break; + case TimChannel::Config::Channel::FOUR: + timhdma.Init.Request = DMA_REQUEST_TIM3_CH4; + break; + } + break; + case TimerHandle::Config::Peripheral::TIM_4: + switch(cfg_.chn) + { + case TimChannel::Config::Channel::ONE: + timhdma.Init.Request = DMA_REQUEST_TIM4_CH1; + break; + case TimChannel::Config::Channel::TWO: + timhdma.Init.Request = DMA_REQUEST_TIM4_CH2; + break; + case TimChannel::Config::Channel::THREE: + timhdma.Init.Request = DMA_REQUEST_TIM4_CH3; + break; + case TimChannel::Config::Channel::FOUR: + timhdma.Init.Request + = DMA_REQUEST_TIM4_UP; /**< TIM4_CH4 DMA Rq doesn't exist?*/ + break; + } + break; + case TimerHandle::Config::Peripheral::TIM_5: + switch(cfg_.chn) + { + case TimChannel::Config::Channel::ONE: + timhdma.Init.Request = DMA_REQUEST_TIM5_CH1; + break; + case TimChannel::Config::Channel::TWO: + timhdma.Init.Request = DMA_REQUEST_TIM5_CH2; + break; + case TimChannel::Config::Channel::THREE: + timhdma.Init.Request = DMA_REQUEST_TIM5_CH3; + break; + case TimChannel::Config::Channel::FOUR: + timhdma.Init.Request = DMA_REQUEST_TIM5_CH4; + break; + } + break; + } + timhdma.Init.Direction = DMA_MEMORY_TO_PERIPH; timhdma.Init.PeriphInc = DMA_PINC_DISABLE; @@ -142,14 +217,28 @@ void TimChannel::StartDma(void* data, size_t size, void* callback) timhdma.Init.FIFOMode = DMA_FIFOMODE_DISABLE; timhdma.Init.MemBurst = DMA_MBURST_SINGLE; timhdma.Init.PeriphBurst = DMA_PBURST_SINGLE; - if(HAL_DMA_Init(&timhdma) != HAL_OK) { // something bad } SetInstance(&globaltim, cfg_.tim->GetConfig().periph); - __HAL_LINKDMA(&globaltim, hdma[TIM_DMA_ID_CC4], timhdma); - HAL_TIM_PWM_Start_DMA(&globaltim, GetHalChannel(cfg_.chn), (uint32_t*)data, size); + switch(cfg_.chn) + { + case TimChannel::Config::Channel::ONE: + __HAL_LINKDMA(&globaltim, hdma[TIM_DMA_ID_CC1], timhdma); + break; + case TimChannel::Config::Channel::TWO: + __HAL_LINKDMA(&globaltim, hdma[TIM_DMA_ID_CC2], timhdma); + break; + case TimChannel::Config::Channel::THREE: + __HAL_LINKDMA(&globaltim, hdma[TIM_DMA_ID_CC3], timhdma); + break; + case TimChannel::Config::Channel::FOUR: + __HAL_LINKDMA(&globaltim, hdma[TIM_DMA_ID_CC4], timhdma); + break; + } + HAL_TIM_PWM_Start_DMA( + &globaltim, GetHalChannel(cfg_.chn), (uint32_t*)data, size); } From 91996a8314631fb4426655bafff68589c4bd3f35 Mon Sep 17 00:00:00 2001 From: stephenhensley Date: Thu, 8 Dec 2022 17:03:27 -0800 Subject: [PATCH 10/11] workin on it --- .../PWM/AddressableLED/addressable-led.cpp | 93 ++++++++++++------- src/per/tim_channel.cpp | 27 +++++- src/per/tim_channel.h | 26 +++++- 3 files changed, 109 insertions(+), 37 deletions(-) diff --git a/examples/PWM/AddressableLED/addressable-led.cpp b/examples/PWM/AddressableLED/addressable-led.cpp index 11ae46130..a40d7a52e 100644 --- a/examples/PWM/AddressableLED/addressable-led.cpp +++ b/examples/PWM/AddressableLED/addressable-led.cpp @@ -18,10 +18,22 @@ DaisySeed hw; const size_t kOutBufferSize = 512; uint32_t DMA_BUFFER_MEM_SECTION outbuffer[kOutBufferSize]; +// const size_t kTickPeriod = 36; +// const int kOneTime = kTickPeriod - 20; +// const int kZeroTime = kTickPeriod - 8; +const size_t kTickPeriod = 29; +// const size_t kTickPeriod = 59; const int kOneTime = 20; const int kZeroTime = 8; -const int kNumLeds = 12; + +/** both the same for now*/ +// const int kZeroTime = 14; +// const int kOneTime = 21; +// const int kZeroTime = 2 * (kTickPeriod / 3); +// const int kOneTime = kTickPeriod / 3; + +const int kNumLeds = 4; uint8_t led_data[kNumLeds][3]; /**< RGB data */ const size_t kOutDataSize = kNumLeds * 3 * 8; @@ -30,11 +42,11 @@ uint32_t DMA_BUFFER_MEM_SECTION * 8]; /**< PWM lengths data, one "duration" per bit */ /** buff expects that 8 elements are available for the one 8-bit color val*/ -static void populate_bits(uint8_t color_val, uint32_t *buff) +static void populate_bits(uint8_t color_val, uint32_t* buff) { for(int i = 0; i < 8; i++) { - buff[i] = (color_val & i) > 0 ? kOneTime : kZeroTime; + buff[7 - i] = (color_val & i) > 0 ? kOneTime : kZeroTime; } } @@ -66,6 +78,13 @@ static void set_led_f(int index, float r, float g, float b) set_led(index, r * 255, g * 255, b * 255); } +void EndOfLeds(void* context) +{ + TimChannel* pwm = (TimChannel*)context; + pwm->SetPwm(0); + // pwm->Stop(); +} + int main(void) { /** Initialize hardware */ @@ -75,6 +94,7 @@ int main(void) TimerHandle::Config tim_cfg; TimerHandle timer; tim_cfg.periph = TimerHandle::Config::Peripheral::TIM_4; + tim_cfg.dir = TimerHandle::Config::CounterDir::UP; timer.Init(tim_cfg); /** Generate period for timer @@ -88,10 +108,11 @@ int main(void) timer.SetPeriod(period); TimChannel::Config chn_cfg; - chn_cfg.tim = &timer; - chn_cfg.chn = TimChannel::Config::Channel::ONE; - chn_cfg.mode = TimChannel::Config::Mode::PWM; - chn_cfg.pin = seed::D13; + chn_cfg.tim = &timer; + chn_cfg.chn = TimChannel::Config::Channel::ONE; + chn_cfg.mode = TimChannel::Config::Mode::PWM; + chn_cfg.polarity = TimChannel::Config::Polarity::HIGH; + chn_cfg.pin = seed::D13; TimChannel pwm; /** Fill Buffer */ for(size_t i = 0; i < kOutBufferSize; i++) @@ -102,50 +123,60 @@ int main(void) } /** Initialize PWM */ pwm.Init(chn_cfg); + pwm.SetPwm(0); + pwm.Start(); timer.Start(); - //pwm.Start(); - // System::Delay(1000); - // pwm.StartDma(outbuffer, kOutBufferSize, nullptr); - // bool led_state = true; uint32_t now, tled; now = tled = System::GetNow(); + float gbright = 0; + while(1) { - // System::Delay(2000); - // hw.SetLed(led_state); - // if(led_state) - // led_state = false; - // else - // led_state = true; - // pwm.StartDma(outbuffer, kOutBufferSize, nullptr); - now = System::GetNow(); + //if(now - tled > 33) if(now - tled > 33) { tled = now; + // gbright += 0.0005f; + // if(gbright > 0.5f) + // { + // gbright = 0.f; + // } + // gbright += 0.001; + // if(gbright > 1.f) + // { + // gbright = 0.f; + // } + + gbright = 0.03f; /* Lets set some LED stuff */ for(int i = 0; i < kNumLeds; i++) { - float bright = (float)(now & 1023) / 1023.f; - set_led_f(i, bright, bright, bright); + // float bright = (float)(now & 1023) / 1023.f; + // float bright = (now & 1023) > 511 ? 0.33f : 0.f; + switch(i) + { + case 0: set_led_f(i, gbright, 0.f, 0.f); break; + case 1: set_led_f(i, 0.f, gbright, 0.f); break; + case 2: set_led_f(i, 0.f, 0.f, gbright); break; + default: set_led_f(i, gbright, gbright, gbright); break; + } + // switch(i) + // { + // case 0: set_led(i, gbright, 0, 0); break; + // case 1: set_led(i, 0, gbright, 0); break; + // case 2: set_led(i, 0, 0, gbright); break; + // default: set_led(i, gbright, gbright, gbright); break; + // } } fill_led_data(); /** And transmit */ pwm.Start(); - pwm.StartDma(output_data, kOutDataSize, nullptr); - /** when its done..... - * we need to set pwm to 0% or pull low for >=80us - * this is hacky, gross, and would be the perfect thing to setup in the callback... - * - * It should _never_ take more than 1ms nevermind 3.. - * You'd need at least 36 LEDs for it to be 1ms of transmission. - */ - System::Delay(3); - pwm.Stop(); + pwm.StartDma(output_data, kOutDataSize, EndOfLeds, (void*)&pwm); } } } diff --git a/src/per/tim_channel.cpp b/src/per/tim_channel.cpp index 1470d54ea..27a3b6f77 100644 --- a/src/per/tim_channel.cpp +++ b/src/per/tim_channel.cpp @@ -78,7 +78,9 @@ void TimChannel::Init(const TimChannel::Config& cfg) TIM_OC_InitTypeDef sConfigOC = {0}; sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; - sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; + sConfigOC.OCPolarity = cfg.polarity == Config::Polarity::HIGH + ? TIM_OCPOLARITY_HIGH + : TIM_OCPOLARITY_LOW; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; auto chn = GetHalChannel(cfg.chn); TIM_HandleTypeDef tim; @@ -96,7 +98,7 @@ void TimChannel::Init(const TimChannel::Config& cfg) gpio_init.Pin = pin; gpio_init.Mode = GPIO_MODE_AF_PP; gpio_init.Pull = GPIO_NOPULL; - gpio_init.Speed = GPIO_SPEED_MEDIUM; + gpio_init.Speed = GPIO_SPEED_LOW; gpio_init.Alternate = af_value; HAL_GPIO_Init(port, &gpio_init); } @@ -125,12 +127,19 @@ void TimChannel::SetPwm(uint32_t val) SetInstance(&tim, cfg_.tim->GetConfig().periph); __HAL_TIM_SET_COMPARE(&tim, GetHalChannel(cfg_.chn), val); } -static TIM_HandleTypeDef globaltim; +static TIM_HandleTypeDef globaltim; +TimChannel::EndTransmissionFunctionPtr globalcb; +void* globalcb_context; -void TimChannel::StartDma(void* data, size_t size, void* callback) +void TimChannel::StartDma(void* data, + size_t size, + TimChannel::EndTransmissionFunctionPtr callback, + void* cb_context) { timhdma.Instance = DMA2_Stream5; + globalcb = callback; + globalcb_context = cb_context; /** kind of nuts to set this.... */ switch(cfg_.tim->GetConfig().periph) @@ -249,6 +258,16 @@ extern "C" void DMA2_Stream5_IRQHandler(void) HAL_DMA_IRQHandler(&timhdma); } + +extern "C" void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef* htim) +{ + if(globalcb) + { + globalcb(globalcb_context); + } +} + + extern "C" void DMAMUX1_OVR_IRQHandler(void) {} } // namespace daisy \ No newline at end of file diff --git a/src/per/tim_channel.h b/src/per/tim_channel.h index b8a4e7c4a..3b9076403 100644 --- a/src/per/tim_channel.h +++ b/src/per/tim_channel.h @@ -40,11 +40,24 @@ class TimChannel ONE_PULSE, }; + enum class Polarity + { + HIGH, + LOW + }; + TimerHandle* tim; Channel chn; Mode mode; + Polarity polarity; Pin pin; - Config() : tim(nullptr), chn(Channel::ONE), mode(Mode::PWM) {} + Config() + : tim(nullptr), + chn(Channel::ONE), + mode(Mode::PWM), + polarity(Polarity::LOW) + { + } }; TimChannel() {} @@ -59,12 +72,21 @@ class TimChannel /** Stops the PWM output on the given channel's pin */ void Stop(); + /** Sets the immediate PWM value, based on the current TIM period. + * For example, if period is 256, then a val of 128 will be 50% pulsewidth + */ void SetPwm(uint32_t val); + typedef void (*EndTransmissionFunctionPtr)(void* context); + + /** Starts the DMA for the given buffer, calling the callback * when the transmission is complete. */ - void StartDma(void* data, size_t size, void* callback); + void StartDma(void* data, + size_t size, + EndTransmissionFunctionPtr callback = nullptr, + void* cb_context = nullptr); const Config& GetConfig() const; From 1bcfbfe6419c63925d39173a8adfb1a978207b02 Mon Sep 17 00:00:00 2001 From: stephenhensley Date: Tue, 13 Dec 2022 10:08:54 -0800 Subject: [PATCH 11/11] LEDs are working with inverting circuit now as well. --- .../PWM/AddressableLED/addressable-led.cpp | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/examples/PWM/AddressableLED/addressable-led.cpp b/examples/PWM/AddressableLED/addressable-led.cpp index a40d7a52e..e3c87218a 100644 --- a/examples/PWM/AddressableLED/addressable-led.cpp +++ b/examples/PWM/AddressableLED/addressable-led.cpp @@ -46,7 +46,7 @@ static void populate_bits(uint8_t color_val, uint32_t* buff) { for(int i = 0; i < 8; i++) { - buff[7 - i] = (color_val & i) > 0 ? kOneTime : kZeroTime; + buff[i] = (color_val & (1 << (7 - i))) > 0 ? kOneTime : kZeroTime; } } @@ -111,7 +111,7 @@ int main(void) chn_cfg.tim = &timer; chn_cfg.chn = TimChannel::Config::Channel::ONE; chn_cfg.mode = TimChannel::Config::Mode::PWM; - chn_cfg.polarity = TimChannel::Config::Polarity::HIGH; + chn_cfg.polarity = TimChannel::Config::Polarity::LOW; chn_cfg.pin = seed::D13; TimChannel pwm; /** Fill Buffer */ @@ -144,13 +144,14 @@ int main(void) // { // gbright = 0.f; // } - // gbright += 0.001; - // if(gbright > 1.f) - // { - // gbright = 0.f; - // } + gbright += 0.01; + if(gbright > 0.5f) + { + gbright = 0.f; + } - gbright = 0.03f; + // gbright = 0.03f; + // gbright = 1; /* Lets set some LED stuff */ for(int i = 0; i < kNumLeds; i++) @@ -160,8 +161,8 @@ int main(void) switch(i) { case 0: set_led_f(i, gbright, 0.f, 0.f); break; - case 1: set_led_f(i, 0.f, gbright, 0.f); break; - case 2: set_led_f(i, 0.f, 0.f, gbright); break; + case 1: set_led_f(i, 0.f, 0.f, gbright); break; + case 2: set_led_f(i, 0.f, gbright, 0.f); break; default: set_led_f(i, gbright, gbright, gbright); break; } // switch(i)