-
Notifications
You must be signed in to change notification settings - Fork 148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Hardware PWM #568
Draft
stephenhensley
wants to merge
11
commits into
master
Choose a base branch
from
feat/timchn-pwm
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
3e47dd6
added tim_channel class.
f912c8f
missing extern in hal map files
f705e1a
pwm example and updated launch.json to test it
07340c9
cleaned up, and fixed example to work with D17
stephenhensley c055da7
smoothed up the example a little bit
stephenhensley cf03fb8
really hacking DMA in there for now as proof of concept.. needs sever…
127f838
dma example workingg, and a copy to proof of concept digital LED stuff.
b255c86
started putting together some stuff to drive the LED data via pulsewi…
7fc733b
temp. added some switch cases for DMA request, etc to resolve hard co…
91996a8
workin on it
1bcfbfe
LEDs are working with inverting circuit now as well.
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -130,5 +130,5 @@ vs/*.log | |
|
||
tests/libDaisy_gtest | ||
tests/build/bin/ | ||
examples/*/build/ | ||
examples/**/build/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
/** 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..) | ||
* | ||
* 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" | ||
|
||
using namespace daisy; | ||
|
||
/** Hardware object for communicating with Daisy */ | ||
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; | ||
|
||
|
||
/** 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; | ||
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 & (1 << (7 - i))) > 0 ? kOneTime : kZeroTime; | ||
} | ||
} | ||
|
||
static void fill_led_data() | ||
{ | ||
/** TODO fix these to be accurate for necessary timing */ | ||
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); | ||
} | ||
|
||
void EndOfLeds(void* context) | ||
{ | ||
TimChannel* pwm = (TimChannel*)context; | ||
pwm->SetPwm(0); | ||
// pwm->Stop(); | ||
} | ||
|
||
int main(void) | ||
{ | ||
/** Initialize hardware */ | ||
hw.Init(); | ||
|
||
/** Initialize timer */ | ||
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 | ||
* 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::ONE; | ||
chn_cfg.mode = TimChannel::Config::Mode::PWM; | ||
chn_cfg.polarity = TimChannel::Config::Polarity::LOW; | ||
chn_cfg.pin = seed::D13; | ||
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); | ||
pwm.SetPwm(0); | ||
pwm.Start(); | ||
timer.Start(); | ||
|
||
uint32_t now, tled; | ||
now = tled = System::GetNow(); | ||
|
||
float gbright = 0; | ||
|
||
while(1) | ||
{ | ||
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.01; | ||
if(gbright > 0.5f) | ||
{ | ||
gbright = 0.f; | ||
} | ||
|
||
// gbright = 0.03f; | ||
// gbright = 1; | ||
|
||
/* Lets set some LED stuff */ | ||
for(int i = 0; i < kNumLeds; i++) | ||
{ | ||
// 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, 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) | ||
// { | ||
// 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, EndOfLeds, (void*)&pwm); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Project Name | ||
TARGET = basic-pwm | ||
|
||
# Sources | ||
CPP_SOURCES = basic-pwm.cpp | ||
|
||
DEBUG=1 | ||
OPT=-O0 | ||
|
||
# Library Locations | ||
LIBDAISY_DIR = ../../.. | ||
|
||
# Core location, and generic Makefile. | ||
SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core | ||
include $(SYSTEM_FILES_DIR)/Makefile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/** 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; | ||
|
||
int main(void) | ||
{ | ||
/** Initialize hardware */ | ||
hw.Init(); | ||
|
||
/** Configure frequency (12kHz) */ | ||
auto tim_target_freq = 12000; | ||
auto tim_base_freq = System::GetPClk2Freq(); | ||
|
||
TimerHandle::Config tim_cfg; | ||
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 = &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); | ||
timer.Start(); | ||
pwm.Start(); | ||
|
||
/** Step through some brightness values */ | ||
int vals[32]; | ||
for(int i = 0; i < 32; i++) | ||
vals[i] = i * (tim_cfg.period / 32); | ||
|
||
while(1) | ||
{ | ||
/* ~30Hz animation of our little 32-frame ramp wave. */ | ||
for(int i = 0; i < 32; i++) | ||
{ | ||
pwm.SetPwm(vals[i]); | ||
System::Delay(33); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make sense here to provide a number of examples alongside different LED controllers. I know there are minor differences between vendors (though mostly it comes out in the wash).
For my example with one of either the WS2812B or SK6812 (unknown which I have on these "neopixel" items), I had MUCH higher reliability with many neopixels (28) setting
kOneTime
to14
andkZeroTime
to9
, while changing line 114 tochn_cfg.polarity = TimChannel::Config::Polarity::HIGH;
(from ::LOW).I haven't analyzed the waveform yet to determine exactly how the polarity affects things, but my results were VERY unreliable before I set polarity ::HIGH.