-
-
Notifications
You must be signed in to change notification settings - Fork 345
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement HardwarePWM class for Rp2040 and update Basic PWM sample (#…
- Loading branch information
Showing
10 changed files
with
259 additions
and
298 deletions.
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
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 |
---|---|---|
@@ -1,88 +1,8 @@ | ||
#pragma once | ||
|
||
#if defined(__cplusplus) | ||
extern "C" { | ||
#endif | ||
|
||
// #include <pwm.h> | ||
|
||
#define PWM_CHANNEL_NUM_MAX 16 | ||
|
||
/** | ||
* @defgroup pwm_driver PWM driver | ||
* @ingroup drivers | ||
* @{ | ||
*/ | ||
|
||
/** | ||
* @fn void pwm_init(uint32 period, uint32 *duty,uint32 pwm_channel_num,uint32 (*pin_info_list)[3]) | ||
* @brief Initialize PWM function, including GPIO selection, period and duty cycle | ||
* @param period PWM period | ||
* @param duty duty cycle of each output | ||
* @param pwm_channel_num PWM channel number | ||
* @param pin_info_list Array containing an entry for each channel giving | ||
* @note This API can be called only once. | ||
* | ||
* Example: | ||
* | ||
* uint32 io_info[][3] = { | ||
* {PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC, PWM_0_OUT_IO_NUM}, | ||
* {PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC, PWM_1_OUT_IO_NUM}, | ||
* {PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC, PWM_2_OUT_IO_NUM} | ||
* }; | ||
* | ||
* pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, io_info); | ||
* | ||
*/ | ||
|
||
/** | ||
* @fn void pwm_start(void) | ||
* @brief Starts PWM | ||
* @brief Maximum number of active PWM channels. | ||
* | ||
* This function needs to be called after PWM configuration is changed. | ||
* The Pico has 8 PWM 'slices', each of which can drive two outputs. | ||
*/ | ||
|
||
/** | ||
* @fn void pwm_set_duty(uint32 duty, uint8 channel) | ||
* @brief Sets duty cycle of a PWM output | ||
* @param duty The time that high-level single will last, duty cycle will be (duty*45)/(period*1000) | ||
* @param channel PWM channel, which depends on how many PWM channels are used | ||
* | ||
* Set the time that high-level signal will last. | ||
* The range of duty depends on PWM period. Its maximum value of which can be Period * 1000 / 45. | ||
* | ||
* For example, for 1-KHz PWM, the duty range is 0 ~ 22222. | ||
*/ | ||
|
||
/** | ||
* @fn uint32 pwm_get_duty(uint8 channel) | ||
* @brief Get duty cycle of PWM output | ||
* @param channel PWM channel, which depends on how many PWM channels are used | ||
* @retval uint32 Duty cycle of PWM output | ||
* | ||
* Duty cycle will be (duty*45) / (period*1000). | ||
*/ | ||
|
||
/** | ||
* @fn void pwm_set_period(uint32 period) | ||
* @brief Set PWM period | ||
* @param period PWM period in us. For example, 1-KHz PWM period = 1000us. | ||
*/ | ||
|
||
/** | ||
* @fn uint32 pwm_get_period(void) | ||
* @brief Get PWM period | ||
* @retval uint32 Return PWM period in us. | ||
*/ | ||
|
||
/** | ||
* @fn uint32 get_pwm_version(void) | ||
* @brief Get version information of PWM | ||
* @retval uint32 PWM version | ||
*/ | ||
|
||
/** @} */ | ||
|
||
#if defined(__cplusplus) | ||
} | ||
#endif | ||
#define PWM_CHANNEL_NUM_MAX 16 |
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,135 @@ | ||
/**** | ||
* Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. | ||
* Created 2015 by Skurydin Alexey | ||
* http://github.com/SmingHub/Sming | ||
* All files of the Sming Core are provided under the LGPL v3 license. | ||
* | ||
* Rp2040/Core/HardwarePWM.cpp | ||
* | ||
*/ | ||
|
||
#include <HardwarePWM.h> | ||
#include <hardware/pwm.h> | ||
#include <hardware/gpio.h> | ||
#include <hardware/clocks.h> | ||
#include <muldiv.h> | ||
#include <algorithm> | ||
#include <debug_progmem.h> | ||
|
||
/* | ||
* Note on use of divisor | ||
* ---------------------- | ||
* | ||
* Divisor is 8:4 fractional value, so 1 <= int <= 255, 0 <= frac <= 15 | ||
* Simplest way to use full range is to factor calculations by 16. | ||
* | ||
* Using default CSR_PH_CORRECT=0: | ||
* | ||
* F_PWM = 16 * F_SYS / (TOP + 1) / DIV | ||
* | ||
*/ | ||
|
||
#define PWM_FREQ_DEFAULT 1000 | ||
|
||
HardwarePWM::HardwarePWM(const uint8_t* pins, uint8_t noOfPins) : channel_count(noOfPins) | ||
{ | ||
assert(noOfPins > 0 && noOfPins <= PWM_CHANNEL_NUM_MAX); | ||
noOfPins = std::min(uint8_t(PWM_CHANNEL_NUM_MAX), noOfPins); | ||
std::copy_n(pins, noOfPins, channels); | ||
setPeriod(1e6 / PWM_FREQ_DEFAULT); | ||
|
||
for(unsigned i = 0; i < noOfPins; ++i) { | ||
auto pin = channels[i]; | ||
gpio_set_function(pin, GPIO_FUNC_PWM); | ||
gpio_set_dir(pin, GPIO_OUT); | ||
} | ||
} | ||
|
||
HardwarePWM::~HardwarePWM() | ||
{ | ||
for(unsigned i = 0; i < channel_count; ++i) { | ||
auto slice_num = pwm_gpio_to_slice_num(channels[i]); | ||
pwm_set_enabled(slice_num, false); | ||
} | ||
} | ||
|
||
uint32_t HardwarePWM::getDutyChan(uint8_t chan) const | ||
{ | ||
if(chan >= channel_count) { | ||
return 0; | ||
} | ||
auto pin = channels[chan]; | ||
auto slice_num = pwm_gpio_to_slice_num(pin); | ||
auto value = pwm_hw->slice[slice_num].cc; | ||
value >>= pwm_gpio_to_channel(pin) ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB; | ||
return value & 0xffff; | ||
} | ||
|
||
bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool) | ||
{ | ||
if(chan >= channel_count) { | ||
return false; | ||
} | ||
auto pin = channels[chan]; | ||
duty = std::min(duty, maxduty); | ||
pwm_set_gpio_level(pin, duty); | ||
return true; | ||
} | ||
|
||
uint32_t HardwarePWM::getPeriod() const | ||
{ | ||
// All channels configured with same clock | ||
auto slice_num = pwm_gpio_to_slice_num(channels[0]); | ||
uint32_t top = pwm_hw->slice[slice_num].top; | ||
uint32_t div = pwm_hw->slice[slice_num].div; | ||
return muldiv(62500ULL, (top + 1) * div, clock_get_hz(clk_sys)); | ||
} | ||
|
||
void HardwarePWM::setPeriod(uint32_t period) | ||
{ | ||
const uint32_t topMax{0xffff}; | ||
const uint32_t divMin{0x10}; // 1.0 | ||
const uint32_t divMax{0xfff}; // INT + FRAC | ||
auto sysFreq = clock_get_hz(clk_sys); | ||
// Calculate divisor assuming maximum value for TOP: ensure value is rounded UP | ||
uint32_t div = ((uint64_t(period) * sysFreq / 62500) + topMax) / (topMax + 1); | ||
uint32_t top; | ||
if(div > divMax) { | ||
// Period too big, set to maximum | ||
top = topMax; | ||
div = divMax; | ||
} else { | ||
if(div < divMin) { | ||
// Period is very small, set div to minimum | ||
div = divMin; | ||
} | ||
top = (uint64_t(period) * sysFreq / 62500 / div) - 1; | ||
} | ||
|
||
debug_d("[PWM] %s(%u): div %u, top %u", __FUNCTION__, period, div, top); | ||
|
||
pwm_config cfg = pwm_get_default_config(); | ||
cfg.div = div; | ||
cfg.top = top; | ||
|
||
for(unsigned i = 0; i < channel_count; ++i) { | ||
auto pin = channels[i]; | ||
auto slice_num = pwm_gpio_to_slice_num(pin); | ||
pwm_init(slice_num, &cfg, true); | ||
} | ||
|
||
maxduty = top; | ||
} | ||
|
||
void HardwarePWM::update() | ||
{ | ||
// Not implemented | ||
} | ||
|
||
uint32_t HardwarePWM::getFrequency(uint8_t pin) const | ||
{ | ||
auto slice_num = pwm_gpio_to_slice_num(pin); | ||
auto top = pwm_hw->slice[slice_num].top; | ||
auto div = pwm_hw->slice[slice_num].div; | ||
return 16UL * clock_get_hz(clk_sys) / (div * (top + 1)); | ||
} |
Oops, something went wrong.