123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- /*
- * Software PWM using soft-interrupt timer1.
- * Supports higher frequencies compared to Espressif provided one.
- *
- * Nikolay Fiykov
- */
- #include <stddef.h>
- #include <stdint.h>
- #include <string.h>
- #include "mem.h"
- #include "pin_map.h"
- #include "platform.h"
- #include "hw_timer.h"
- #include "driver/pwm2.h"
- #include "user_interface.h"
- #define PWM2_TMR_MAGIC_80MHZ 16
- #define PWM2_TMR_MAGIC_160MHZ 32
- // module vars, lazy initialized, allocated only if pwm2 is being used
- static pwm2_module_data_t *moduleData = NULL;
- //############################
- // tools
- static bool isPinSetup(const pwm2_module_data_t *data, const uint8_t pin) {
- return data->setupData.pin[pin].pulseResolutions > 0;
- }
- static uint32_t getCPUTicksPerSec() {
- return system_get_cpu_freq() * 1000000;
- }
- static uint8_t getCpuTimerTicksDivisor() {
- return system_get_cpu_freq() == 80 ? PWM2_TMR_MAGIC_80MHZ : PWM2_TMR_MAGIC_160MHZ;
- }
- static uint32_t findGCD(uint32_t n1, uint32_t n2) {
- uint32_t n3;
- while (n2 != 0) {
- n3 = n1;
- n1 = n2;
- n2 = n3 % n2;
- }
- return n1;
- }
- static uint32_t findGreatesCommonDividerForTimerTicks(uint32_t newTimerTicks, uint32_t oldTimerTicks) {
- return oldTimerTicks == 0 ? newTimerTicks : findGCD(newTimerTicks, oldTimerTicks);
- }
- static uint16_t findAllEnabledGpioMask(pwm2_module_data_t *moduleData) {
- uint16_t enableGpioMask = 0;
- for (int i = 1; i < GPIO_PIN_NUM; i++) {
- if (moduleData->setupData.pin[i].pulseResolutions > 0) {
- enableGpioMask |= moduleData->interruptData.pin[i].gpioMask;
- }
- }
- return enableGpioMask;
- }
- static uint32_t findCommonCPUTicksDivisor(pwm2_module_data_t *moduleData) {
- uint32_t gcdCPUTicks = 0;
- for (int i = 1; i < GPIO_PIN_NUM; i++) {
- if (moduleData->setupData.pin[i].pulseResolutions > 0) {
- gcdCPUTicks = findGreatesCommonDividerForTimerTicks(moduleData->setupData.pin[i].resolutionCPUTicks, gcdCPUTicks);
- }
- }
- return gcdCPUTicks;
- }
- static uint32_t cpuToTimerTicks(uint32_t cpuTicks) {
- return cpuTicks / getCpuTimerTicksDivisor();
- }
- static void updatePinResolutionToInterruptsMultiplier(pwm2_pin_setup_t *sPin, uint32_t timerCPUTicks) {
- sPin->resolutionInterruptCounterMultiplier = sPin->resolutionCPUTicks / timerCPUTicks;
- }
- static void updatePinPulseToInterruptsCounter(pwm2_pin_interrupt_t *iPin, pwm2_pin_setup_t *sPin) {
- iPin->pulseInterruptCcounter = (sPin->pulseResolutions + 1) * sPin->resolutionInterruptCounterMultiplier;
- }
- static uint8_t getDutyAdjustment(const uint32_t duty, const uint32_t pulse) {
- if (duty == 0) {
- return 0;
- } else if (duty == pulse) {
- return 2;
- } else {
- return 1;
- }
- }
- static void updatePinOffCounter(pwm2_pin_interrupt_t *iPin, pwm2_pin_setup_t *sPin) {
- iPin->offInterruptCounter = (sPin->duty + getDutyAdjustment(sPin->duty, sPin->pulseResolutions)) * sPin->resolutionInterruptCounterMultiplier;
- }
- static void reCalculateCommonToAllPinsData(pwm2_module_data_t *moduleData) {
- moduleData->interruptData.enabledGpioMask = findAllEnabledGpioMask(moduleData);
- moduleData->setupData.interruptTimerCPUTicks = findCommonCPUTicksDivisor(moduleData);
- moduleData->setupData.interruptTimerTicks = cpuToTimerTicks(moduleData->setupData.interruptTimerCPUTicks);
- for (int i = 1; i < GPIO_PIN_NUM; i++) {
- if (isPinSetup(moduleData, i)) {
- updatePinResolutionToInterruptsMultiplier(&moduleData->setupData.pin[i], moduleData->setupData.interruptTimerCPUTicks);
- updatePinPulseToInterruptsCounter(&moduleData->interruptData.pin[i], &moduleData->setupData.pin[i]);
- updatePinOffCounter(&moduleData->interruptData.pin[i], &moduleData->setupData.pin[i]);
- }
- }
- }
- static uint64_t enduserFreqToCPUTicks(const uint64_t divisableFreq, const uint64_t freqDivisor, const uint64_t resolution) {
- return (getCPUTicksPerSec() / (freqDivisor * resolution)) * divisableFreq;
- }
- static uint16_t getPinGpioMask(uint8_t pin) {
- return 1 << GPIO_ID_PIN(pin_num[pin]);
- }
- static void set_duty(pwm2_module_data_t *moduleData, const uint8_t pin, const uint32_t duty) {
- pwm2_pin_setup_t *sPin = &moduleData->setupData.pin[pin];
- pwm2_pin_interrupt_t *iPin = &moduleData->interruptData.pin[pin];
- sPin->duty = duty;
- updatePinOffCounter(iPin, sPin);
- }
- static void configureAllPinsAsGpioOutput(pwm2_module_data_t *moduleData) {
- for (int i = 1; i < GPIO_PIN_NUM; i++) {
- if (isPinSetup(moduleData, i)) {
- PIN_FUNC_SELECT(pin_mux[i], pin_func[i]); // set pin as gpio
- PIN_PULLUP_EN(pin_mux[i]); // set pin pullup on
- }
- }
- }
- static void resetPinCounters(pwm2_module_data_t *moduleData) {
- for (int i = 1; i < GPIO_PIN_NUM; i++) {
- if (isPinSetup(moduleData, i)) {
- moduleData->interruptData.pin[i].currentInterruptCounter = 0;
- }
- }
- }
- //############################
- // interrupt handler related
- static inline void computeIsPinOn(pwm2_pin_interrupt_t *pin, uint16_t *maskOn) {
- if (pin->currentInterruptCounter == pin->pulseInterruptCcounter) {
- pin->currentInterruptCounter = 1;
- } else {
- pin->currentInterruptCounter++;
- }
- // ets_printf("curr=%u on=%u\n", pin->currentInterruptCounter, (pin->currentInterruptCounter < pin->offInterruptCounter));
- if (pin->currentInterruptCounter < pin->offInterruptCounter) {
- *maskOn |= pin->gpioMask;
- }
- }
- static inline bool isPinSetup2(const pwm2_interrupt_handler_data_t *data, const uint8_t pin) {
- return data->pin[pin].gpioMask > 0;
- }
- static inline uint16_t findAllPinOns(pwm2_interrupt_handler_data_t *data) {
- uint16_t maskOn = 0;
- for (int i = 1; i < GPIO_PIN_NUM; i++) {
- if (isPinSetup2(data, i)) {
- computeIsPinOn(&data->pin[i], &maskOn);
- }
- }
- return maskOn;
- }
- static inline void setGpioPins(const uint16_t enabledGpioMask, const register uint16_t maskOn) {
- GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, maskOn);
- const register uint16_t maskOff = ~maskOn & enabledGpioMask;
- GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, maskOff);
- }
- static void ICACHE_RAM_ATTR timerInterruptHandler(os_param_t arg) {
- pwm2_interrupt_handler_data_t *data = (pwm2_interrupt_handler_data_t *)arg;
- setGpioPins(data->enabledGpioMask, findAllPinOns(data));
- }
- //############################
- // driver's public API
- void pwm2_init() {
- moduleData = os_malloc(sizeof(pwm2_module_data_t));
- memset(moduleData, 0, sizeof(*moduleData));
- }
- pwm2_module_data_t *pwm2_get_module_data() {
- return moduleData;
- }
- bool pwm2_is_pin_setup(const uint8_t pin) {
- return isPinSetup(moduleData, pin);
- }
- void pwm2_setup_pin(
- const uint8_t pin,
- const uint32_t divisableFreq,
- const uint32_t freqDivisor,
- const uint32_t resolution,
- const uint32_t initDuty
- )
- {
- moduleData->setupData.pin[pin].pulseResolutions = resolution;
- moduleData->setupData.pin[pin].divisableFrequency = divisableFreq;
- moduleData->setupData.pin[pin].frequencyDivisor = freqDivisor;
- moduleData->setupData.pin[pin].resolutionCPUTicks = enduserFreqToCPUTicks(divisableFreq, freqDivisor, resolution);
- moduleData->interruptData.pin[pin].gpioMask = getPinGpioMask(pin);
- reCalculateCommonToAllPinsData(moduleData);
- set_duty(moduleData, pin, initDuty);
- }
- void pwm2_release_pin(const uint8_t pin) {
- moduleData->setupData.pin[pin].pulseResolutions = 0;
- moduleData->interruptData.pin[pin].gpioMask = 0;
- }
- void pwm2_stop() {
- if (!moduleData->setupData.isStarted) {
- return;
- }
- platform_hw_timer_close_exclusive();
- GPIO_REG_WRITE(GPIO_ENABLE_W1TC_ADDRESS, moduleData->interruptData.enabledGpioMask); // clear pins of being gpio output
- moduleData->setupData.isStarted = false;
- }
- bool pwm2_start() {
- if (moduleData->setupData.isStarted) {
- return true;
- }
- if (!platform_hw_timer_init_exclusive(FRC1_SOURCE, TRUE, timerInterruptHandler, (os_param_t)&moduleData->interruptData, (void (*)(void))NULL)) {
- return false;
- }
- configureAllPinsAsGpioOutput(moduleData);
- resetPinCounters(moduleData);
- GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, moduleData->interruptData.enabledGpioMask); // set pins as gpio output
- moduleData->setupData.isStarted = true;
- platform_hw_timer_arm_ticks_exclusive(moduleData->setupData.interruptTimerTicks);
- return true;
- }
- bool pwm2_is_started() {
- return moduleData->setupData.isStarted;
- }
- void pwm2_set_duty(const uint8_t pin, const uint32_t duty) {
- set_duty(moduleData, pin, duty);
- }
|