From 03b05051aab8b758fe244b32a0f851db3e3f0e9e Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Sun, 15 Mar 2026 23:44:58 +0300 Subject: [PATCH] PWM works --- F0:F030,F042,F072/usbcan_gpio/gpio.c | 111 +++++------- F0:F030,F042,F072/usbcan_gpio/gpio.h | 11 -- F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp | 134 +++++++-------- F0:F030,F042,F072/usbcan_gpio/hardware.c | 4 +- F0:F030,F042,F072/usbcan_gpio/pwm.c | 161 +++++++++++++++++- F0:F030,F042,F072/usbcan_gpio/pwm.h | 28 +++ F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin | Bin 24872 -> 25700 bytes .../usbcan_gpio/usbcangpio.creator.user | 2 +- F0:F030,F042,F072/usbcan_gpio/version.inc | 2 +- 9 files changed, 292 insertions(+), 161 deletions(-) diff --git a/F0:F030,F042,F072/usbcan_gpio/gpio.c b/F0:F030,F042,F072/usbcan_gpio/gpio.c index ad1edf9..0dd658f 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpio.c +++ b/F0:F030,F042,F072/usbcan_gpio/gpio.c @@ -22,6 +22,7 @@ #include "adc.h" #include "flash.h" #include "gpio.h" +#include "pwm.h" #include "usart.h" static uint16_t monitor_mask[2] = {0}; // pins to monitor == 1 (ONLY GPIO and ADC) @@ -80,56 +81,6 @@ static const pinprops_t pin_props[2][16] = { #undef _S #undef _I -#if 0 -PWM (start - collisions): -PxN XY (XY: TIMX_CHY) -PA1 22 * -PA2 23 ** -PA3 24 *** -PA6 161 -PA7 141 -PA9 12 -PA10 13 -PB0 33 -PB1 34 -PB3 22 * -PB4 31 -PB5 32 -PB10 23 ** -PB11 24 *** --> need to set up timers / channels -TIM1 / 2 3 -TIM2 / 2 3 4 -TIM3 / 1 2 3 4 -TIM14 / 1 -TIM16 / 1 -#endif - -#define PT(t, ch) {.tim = t, .chidx = ch} -#define PTC(t, ch, P, p) {.tim = t, .chidx = ch, .collision = 1, .collport = P, .collpin = p} -static const pwmtimer_t timer_map[2][16] = { - [0] = { - [1] = PTC(TIM2, 1, 1, 3), - [2] = PTC(TIM2, 2, 1, 10), - [3] = PTC(TIM2, 3, 1, 11), - [6] = PT(TIM16, 0), - [7] = PT(TIM14, 0), - [9] = PT(TIM1, 1), - [10] = PT(TIM1, 2) - }, - [1] = { - [0] = PT(TIM3, 2), - [1] = PT(TIM3, 3), - [3] = PTC(TIM2, 1, 0, 1), - [4] = PT(TIM3, 0), - [5] = PT(TIM3, 1), - [10] = PTC(TIM2, 2, 0, 2), - [11] = PTC(TIM2, 3, 0, 3) - } -}; -#undef PT -#undef PTC - typedef struct{ uint8_t isrx : 1; uint8_t istx : 1; @@ -244,6 +195,21 @@ int chkpinconf(){ if(cfg->monitor) UC.monitor = 1; } break; + case FUNC_PWM:{ + pwmtimer_t pwm; + if(!canPWM(port, pin, &pwm)){ + DBG("Can't PWM\n"); + defconfig(cfg); + ret = FALSE; + break; + } + if(pwm.collision && pinconfig[pwm.collport][pwm.collpin].af == FUNC_PWM){ + DBG("Found collision -> remove\n"); + defconfig(&pinconfig[pwm.collport][pwm.collpin]); // set later collision to defaults + ret = FALSE; + break; + } + } default: break; // later fill other functions } } @@ -252,9 +218,11 @@ int chkpinconf(){ } // now check USART configuration if(active_usart != -1){ + DBG("Got active USART\n"); UC.idx = active_usart; if(!chkusartconf(&UC)) ret = FALSE; }else{ + DBG("No active USARTs\n"); get_defusartconf(&UC); // clear global configuration the_conf.usartconfig = UC; } @@ -361,7 +329,7 @@ int gpio_reinit(){ gpio->OSPEEDR = (gpio->OSPEEDR & ~(3 << shift2)) | (cfg->speed << shift2); gpio->PUPDR = (gpio->PUPDR & ~(3 << shift2)) | (cfg->pull << shift2); if(pin < 8){ - int shift4 = pin << 4; + int shift4 = pin << 2; gpio->AFR[0] = (gpio->AFR[0] & ~(0xf << shift4)) | (cfg->afno << shift4); }else{ int shift4 = (pin - 8) << 2; @@ -380,6 +348,18 @@ int gpio_reinit(){ oldstates[port][pin] = (gpio->IDR >> pin) & 1; } } + // start/stop PWM on this pin + if(cfg->mode == MODE_AF && cfg->af == FUNC_PWM){ + if(!startPWM(port, pin)) ret = FALSE; + }else{ // check for collisions + pwmtimer_t t; + if(canPWM(port, pin, &t)){ + if(t.collision){ // stop PWM only if "earlier" channel don't set on this + if((t.collport < port || t.collpin < pin) && (pinconfig[t.collport][t.collpin].af != FUNC_PWM)) + stopPWM(port, pin); + }else stopPWM(port, pin); + } + } } } // if all OK, copy to the_conf @@ -390,7 +370,7 @@ int gpio_reinit(){ if(get_curusartconf(&usc) && (usc.RXen | usc.TXen)){ if(!usart_config(NULL)) ret = FALSE; else if(!usart_start()) ret = FALSE; - } + }else usart_stop(); return ret; } @@ -434,12 +414,18 @@ int16_t pin_in(uint8_t port, uint8_t pin){ case MODE_OUTPUT: if(GPIOx->IDR & (1<= 0){ - return (int16_t)getADCval(chan); // getADCval возвращает uint16_t - } + if(chan >= 0) + val = (int16_t) getADCval(chan); + } + break; + case MODE_AF:{ + pinconfig_t curconf; + if(!get_curpinconf(port, pin, &curconf)) return -1; + if(curconf.af == FUNC_PWM) + val = getPWM(port, pin); } break; default: @@ -487,16 +473,3 @@ uint16_t gpio_alert(uint8_t port){ return alert; } -/** - * @brief canPWM - check if pin have PWM ability - * @param port - port (0/1 for GPIOA/GPIOB) - * @param pin - pin (0..15) - * @param t (o) - struct for pin's PWM timer - * @return TRUE if can, FALSE if no - */ -int canPWM(uint8_t port, uint8_t pin, pwmtimer_t *t){ - if(port > 1 || pin > 15) return 0; - if(t) *t = timer_map[port][pin]; - if(timer_map[port][pin].tim) return TRUE; - return FALSE; -} diff --git a/F0:F030,F042,F072/usbcan_gpio/gpio.h b/F0:F030,F042,F072/usbcan_gpio/gpio.h index 826471e..2893c68 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpio.h +++ b/F0:F030,F042,F072/usbcan_gpio/gpio.h @@ -91,15 +91,6 @@ typedef struct{ uint16_t threshold; // threshold for ADC measurement } pinconfig_t; -// Timers for PWM -typedef struct{ - volatile TIM_TypeDef *tim; // timer - uint8_t chidx : 2; // channel index (0..3) - uint8_t collision : 1; // have collision with other channel (1) - uint8_t collport : 1; // collision port index (0 - GPIOA, 1 - GPIOB) - uint8_t collpin : 4; // collision pin index (0..15) -} pwmtimer_t; - /* typedef struct{ uint32_t speed; @@ -121,5 +112,3 @@ int gpio_reinit(); int pin_out(uint8_t port, uint8_t pin, uint8_t newval); int16_t pin_in(uint8_t port, uint8_t pin); uint16_t gpio_alert(uint8_t port); - -int canPWM(uint8_t port, uint8_t pin, pwmtimer_t *t); diff --git a/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp index 9cde6ca..03d8255 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp +++ b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp @@ -27,6 +27,7 @@ extern "C"{ #include "flash.h" #include "gpio.h" #include "gpioproto.h" +#include "pwm.h" #include "usart.h" #undef USBIF #define USBIF IGPIO @@ -46,7 +47,8 @@ static uint8_t hex_input_mode = 0; // ==0 for text input, 1 for HEX + text in qu #define COMMAND_TABLE \ COMMAND(canspeed, "CAN bus speed setter/getter (kBaud, 10..1000)") \ COMMAND(curcanspeed,"current CAN bus speed (interface speed, not settings)") \ - COMMAND(dumpconf, "dump current configuration") \ + COMMAND(curpinconf, "dump current (maybe wrong) pin configuration") \ + COMMAND(dumpconf, "dump global configuration") \ COMMAND(eraseflash, "erase full flash storage") \ COMMAND(help, "show this help") \ COMMAND(hexinput, "input is text (0) or hex + text in quotes (1)") \ @@ -127,6 +129,7 @@ KW(AIN) \ KW(SPEED) \ KW(TEXT) \ KW(HEX) \ + KW(PWM) \ enum{ // indexes of string keywords #define KW(k) STR_ ## k, @@ -155,6 +158,7 @@ static const Keyword keywords[] = { KEY(USART, GROUP_FUNC, FUNC_USART) KEY(SPI, GROUP_FUNC, FUNC_SPI) KEY(I2C, GROUP_FUNC, FUNC_I2C) + KEY(PWM, GROUP_FUNC, FUNC_PWM) KEY(MONITOR, GROUP_MISC, MISC_MONITOR) KEY(THRESHOLD, GROUP_MISC, MISC_THRESHOLD) KEY(SPEED, GROUP_MISC, MISC_SPEED) @@ -177,10 +181,10 @@ static const char* errtxt[ERR_AMOUNT] = { static const char *pinhelp = "Pin settings: PXx = MODE PULL OTYPE FUNC MISC (in any sequence), where\n" - " MODE: AIN, IN or OUT (analog in, digital in, output)\n" + " MODE: AIN, IN or OUT (analog in, digital in, output), also AF (automatically set when AF selected)\n" " PULL: PU, PD or FL (pullup, pulldown, no pull - floating)\n" " OTYPE: PP or OD (push-pull or open-drain)\n" - " FUNC: USART or SPI (enable alternate function and configure peripheal)\n" + " FUNC: USART, SPI, I2C or PWM (enable alternate function and configure peripheal)\n" " MISC: MONITOR - send data by USB as only state changed\n" " THRESHOLD (ADC only) - monitoring threshold, ADU\n" " SPEED - interface speed/frequency\n" @@ -294,16 +298,27 @@ static void pin_getter(uint8_t port, uint8_t pin){ // `port` and `pin` are checked in `parse_pin_command` // set GPIO values (if *setter is 0/1) or configure it static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){ - char _1st = *setter; - if(_1st == '0' || _1st == '1'){ // just set/clear pin state; throw out all text after "1"/"0" - DBG("set pin\n"); - if(pin_out(port, pin, _1st - '0')) return ERR_OK; - return ERR_CANTRUN; - } if(strncmp(setter, "help", 4) == 0){ // send PIN help SENDn(pinhelp); return ERR_AMOUNT; } + pinconfig_t curconf; + if(!get_curpinconf(port, pin, &curconf)) return ERR_BADVAL; // copy current config + uint32_t U32; + char *end = getnum(setter, &U32); + if(end != setter && *end == 0){ // number -> set pin/PWM value + if(U32 > 0xff) return ERR_BADVAL; + uint8_t val = (uint8_t) U32; + if(curconf.mode == MODE_OUTPUT){ // set/clear pin + if(U32 > 1) U32 = 1; + DBG("set pin\n"); + if(pin_out(port, pin, val)) return ERR_OK; + return ERR_CANTRUN; + }else if(curconf.mode == MODE_AF && curconf.af == FUNC_PWM){ + if(setPWM(port, pin, val)) return ERR_OK; + return ERR_CANTRUN; + } + } // complex setter: parse properties uint8_t mode_set = 0xFF, pull_set = 0xFF, otype_set = 0xFF, func_set = 0xFF; bool monitor = false; @@ -311,14 +326,12 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){ uint32_t *pending_u32 = NULL; // -//- for uint32_t usartconf_t UsartConf; if(!get_curusartconf(&UsartConf)) return ERR_CANTRUN; - pinconfig_t curconf; - if(!get_curpinconf(port, pin, &curconf)) return ERR_BADVAL; // copy current config #define DELIM_ " ," char *saveptr, *token = strtok_r(setter, DELIM_, &saveptr); while(token){ if(pending_u16){ uint32_t val; - char *end = getnum(token, &val); + end = getnum(token, &val); if(end == token || val > 0xFFFF) return ERR_BADVAL; *pending_u16 = (uint16_t)val; pending_u16 = NULL; // reset @@ -327,7 +340,7 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){ } if(pending_u32){ uint32_t val; - char *end = getnum(token, &val); + end = getnum(token, &val); if(end == token) return ERR_BADVAL; *pending_u32 = val; pending_u32 = NULL; @@ -468,31 +481,22 @@ static errcodes_t cmd_curcanspeed(const char *cmd, char _U_ *args){ return ERR_AMOUNT; } -static errcodes_t cmd_dumpconf(const char _U_ *cmd, char _U_ *args){ - SEND("userconf_sz="); SEND(u2str(the_conf.userconf_sz)); - SEND("\ncurrentconfidx="); SENDn(i2str(currentconfidx)); - for(int i = 0; i < InterfacesAmount; ++i){ - SEND("interface"); PUTCHAR('0' + i); - PUTCHAR('='); - int l = the_conf.iIlengths[i] / 2; - char *ptr = (char*) the_conf.iInterface[i]; - for(int j = 0; j < l; ++j){ - PUTCHAR(*ptr); - ptr += 2; - } - NL(); - } - SEND("canspeed="); SENDn(u2str(the_conf.CANspeed)); - SEND("Pin configuration:\n"); +// dump global pin config (current == 0) or current (==1) +static void dumppinconf(int current){ + if(current) SEND("Current p"); + else PUTCHAR('P'); + SEND("in configuration:\n"); +#define S(k) SEND(str_keywords[STR_ ## k]) +#define SP(k) do{PUTCHAR(' '); S(k);}while(0) for(int port = 0; port < 2; port++){ char port_letter = (port == 0) ? 'A' : 'B'; for(int pin = 0; pin < 16; pin++){ - pinconfig_t *p = &the_conf.pinconfig[port][pin]; + pinconfig_t cur, *p = &cur; + if(current && !get_curpinconf(port, pin, &cur)) continue; // local + if(!current) p = &the_conf.pinconfig[port][pin]; // global if(!p->enable) continue; PUTCHAR('P'); PUTCHAR(port_letter); SEND(i2str(pin)); SEND(EQ); switch(p->mode){ -#define S(k) SEND(str_keywords[STR_ ## k]) -#define SP(k) do{PUTCHAR(' '); S(k);}while(0) case MODE_INPUT: S(IN); if(p->pull == PULL_UP) SP(PU); @@ -519,6 +523,7 @@ static errcodes_t cmd_dumpconf(const char _U_ *cmd, char _U_ *args){ case FUNC_USART: S(USART); break; case FUNC_SPI: S(SPI); break; case FUNC_I2C: S(I2C); break; + case FUNC_PWM: S(PWM); break; default: SEND("UNKNOWN_AF"); } break; @@ -537,6 +542,33 @@ static errcodes_t cmd_dumpconf(const char _U_ *cmd, char _U_ *args){ NL(); } } +#undef S +#undef SP +} + +static errcodes_t cmd_curpinconf(const char _U_ *cmd, char _U_ *args){ + dumppinconf(TRUE); + return ERR_AMOUNT; +} + +static errcodes_t cmd_dumpconf(const char _U_ *cmd, char _U_ *args){ + SEND("userconf_sz="); SEND(u2str(the_conf.userconf_sz)); + SEND("\ncurrentconfidx="); SENDn(i2str(currentconfidx)); + for(int i = 0; i < InterfacesAmount; ++i){ + SEND("interface"); PUTCHAR('0' + i); + PUTCHAR('='); + int l = the_conf.iIlengths[i] / 2; + char *ptr = (char*) the_conf.iInterface[i]; + for(int j = 0; j < l; ++j){ + PUTCHAR(*ptr); + ptr += 2; + } + NL(); + } + SEND("canspeed="); SENDn(u2str(the_conf.CANspeed)); + dumppinconf(FALSE); // global pin config +#define S(k) SEND(str_keywords[STR_ ## k]) +#define SP(k) do{PUTCHAR(' '); S(k);}while(0) usartconf_t U = the_conf.usartconfig; if(U.RXen || U.TXen){ // USART enabled -> tell config S(USART); SEND(EQ); @@ -547,44 +579,9 @@ static errcodes_t cmd_dumpconf(const char _U_ *cmd, char _U_ *args){ else if(!U.TXen && U.RXen) SEND(" RXONLY"); NL(); } - // here are usart/spi/i2c configurations -#if 0 - bool usart_enabled = false; - for (int port = 0; port < 2 && !usart_enabled; port++) { - for (int pin = 0; pin < 16; pin++) { - pinconfig_t *p = &the_conf.pinconfig[port][pin]; - if (p->enable && p->mode == MODE_AF && p->af == FUNC_USART) { - usart_enabled = true; - break; - } - } - } - if (usart_enabled) { - SEND("usart="); - // usart_config (baud, bits, parity, stopbits) - // e.g: SEND(u2str(usart_config.baudrate)); SEND(" "); - // SEND(i2str(usart_config.databits)); PUTCHAR(usart_config.parity); SEND(i2str(usart_config.stopbits)); - NL(); - } - bool spi_enabled = false; - for(int port = 0; port < 2 && !spi_enabled; port++){ - for (int pin = 0; pin < 16; pin++) { - pinconfig_t *p = &the_conf.pinconfig[port][pin]; - if (p->enable && p->mode == MODE_AF && p->af == FUNC_SPI) { - spi_enabled = true; - break; - } - } - } - if (spi_enabled) { - SEND("spi="); - // spi_config (speed, mode) - NL(); - } -#endif - return ERR_AMOUNT; #undef S #undef SP + return ERR_AMOUNT; } static errcodes_t cmd_setiface(const char* cmd, char *args){ @@ -801,6 +798,7 @@ void GPIO_process(){ // starting init by flash settings void GPIO_init(){ gpio_reinit(); + pwm_setup(); usartconf_t usc; if(get_curusartconf(&usc)) usart_text = usc.textproto; } diff --git a/F0:F030,F042,F072/usbcan_gpio/hardware.c b/F0:F030,F042,F072/usbcan_gpio/hardware.c index d225182..8728d16 100644 --- a/F0:F030,F042,F072/usbcan_gpio/hardware.c +++ b/F0:F030,F042,F072/usbcan_gpio/hardware.c @@ -35,8 +35,8 @@ TRUE_INLINE void pins_setup(){ // setup some common GPIO void hardware_setup(){ // enable all active GPIO clocking RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_DMA1EN; - RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_SYSCFGEN; - RCC->APB1ENR |= RCC_APB1ENR_USART2EN; + RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_TIM16EN; + RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN | RCC_APB1ENR_TIM14EN; pins_setup(); adc_setup(); GPIO_init(); diff --git a/F0:F030,F042,F072/usbcan_gpio/pwm.c b/F0:F030,F042,F072/usbcan_gpio/pwm.c index 4e45470..2b0d208 100644 --- a/F0:F030,F042,F072/usbcan_gpio/pwm.c +++ b/F0:F030,F042,F072/usbcan_gpio/pwm.c @@ -16,17 +16,160 @@ * along with this program. If not, see . */ +#include +#include + #include "pwm.h" -/* - * initial setup of all available PWM timers / channels: - * TIM1 / 2 3 - * TIM2 / 2 3 4 - * TIM3 / 1 2 3 4 - * TIM14 / 1 - * TIM16 / 1 - */ +static volatile TIM_TypeDef * const timers[TIMERS_AMOUNT] = { + [TIM1_IDX] = TIM1, + [TIM2_IDX] = TIM2, + [TIM3_IDX] = TIM3, + [TIM14_IDX] = TIM14, + [TIM16_IDX] = TIM16, +}; + +#if 0 +PWM (start - collisions): +PxN XY (XY: TIMX_CHY) +PA1 22 * +PA2 23 ** +PA3 24 *** +PA6 161 +PA7 141 +PA9 12 +PA10 13 +PB0 33 +PB1 34 +PB3 22 * +PB4 31 +PB5 32 +PB10 23 ** +PB11 24 *** +-> need to set up timers / channels +TIM1 / 2 3 +TIM2 / 2 3 4 +TIM3 / 1 2 3 4 +TIM14 / 1 +TIM16 / 1 +#endif + +#define PT(i, ch) {.timidx = i, .chidx = ch} +#define PTC(i, ch, P, p) {.timidx = i, .chidx = ch, .collision = 1, .collport = P, .collpin = p} +static const pwmtimer_t timer_map[2][16] = { + [0] = { + [1] = PTC(TIM2_IDX, 1, 1, 3), + [2] = PTC(TIM2_IDX, 2, 1, 10), + [3] = PTC(TIM2_IDX, 3, 1, 11), + [6] = PT(TIM16_IDX, 0), + [7] = PT(TIM14_IDX, 0), + [9] = PT(TIM1_IDX, 1), + [10] = PT(TIM1_IDX, 2) + }, + [1] = { + [0] = PT(TIM3_IDX, 2), + [1] = PT(TIM3_IDX, 3), + [3] = PTC(TIM2_IDX, 1, 0, 1), + [4] = PT(TIM3_IDX, 0), + [5] = PT(TIM3_IDX, 1), + [10] = PTC(TIM2_IDX, 2, 0, 2), + [11] = PTC(TIM2_IDX, 3, 0, 3) + } +}; +#undef PT +#undef PTC + +// counter of used channels (0 - timer OFF) +static uint8_t channel_counter[TIMERS_AMOUNT] = {0}; void pwm_setup(){ - ; + // setup; start/stop only by user request + for(int i = 1; i < TIMERS_AMOUNT; ++i){ // start from 1 as 0 forbidden + volatile TIM_TypeDef *timer = timers[i]; + timer->CR1 = 0; + timer->PSC = 7; // 6MHz for 23.4kHz PWM + timer->ARR = 254; // 255 == 100% + // PWM mode 1, preload enable + timer->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE | + TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE; + timer->CCMR2 = TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE | + TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE; + timer->BDTR |= TIM_BDTR_MOE; // enable main output (need for some timers) + timer->EGR |= TIM_EGR_UG; // force update generation + } + bzero(channel_counter, sizeof(channel_counter)); +} + +/** + * @brief canPWM - check if pin have PWM ability + * @param port - port (0/1 for GPIOA/GPIOB) + * @param pin - pin (0..15) + * @param t (o) - struct for pin's PWM timer + * @return TRUE if can, FALSE if no + */ +int canPWM(uint8_t port, uint8_t pin, pwmtimer_t *t){ + if(port > 1 || pin > 15) return 0; + if(t) *t = timer_map[port][pin]; + if(timer_map[port][pin].timidx == TIM_UNSUPPORTED) return FALSE; + return TRUE; +} + +/** + * @brief startPWM - run PWM on given port/pin + * @param port + * @param pin + * @return FALSE if unsupported + */ +int startPWM(uint8_t port, uint8_t pin){ + timidx_t idx = timer_map[port][pin].timidx; + if(idx == TIM_UNSUPPORTED) return FALSE; + volatile TIM_TypeDef *timer = timers[idx]; + uint8_t chidx = timer_map[port][pin].chidx; + uint32_t chen = TIM_CCER_CC1E << (chidx<<2); + if(0 == (timer->CCER & chen)){ + if(0 == channel_counter[idx]++) timer->CR1 |= TIM_CR1_CEN; // start timer if need + timer->CCER |= chen; // enable channel + } + return TRUE; +} + +// stop given PWM channel and stop timer if there's no used channels +void stopPWM(uint8_t port, uint8_t pin){ + timidx_t idx = timer_map[port][pin].timidx; + if(idx == TIM_UNSUPPORTED) return; + volatile TIM_TypeDef *timer = timers[idx]; + uint8_t chidx = timer_map[port][pin].chidx; + uint32_t chen = TIM_CCER_CC1E << (chidx<<2); + if(timer->CCER & chen){ + if(0 == --channel_counter[idx]) timer->CR1 &= ~TIM_CR1_CEN; // stop timer + timer->CCER &= ~chen; + } +} + +/** + * @brief setPWM - set PWM value for given pin on given port + * @param port + * @param pin + * @param val - 0..255 + * @return FALSE if pin can't PWM + */ +int setPWM(uint8_t port, uint8_t pin, uint8_t val){ + timidx_t idx = timer_map[port][pin].timidx; + if(idx == TIM_UNSUPPORTED) return FALSE; + volatile uint32_t *CCR = &timers[idx]->CCR1 + timer_map[port][pin].chidx; + *CCR = val; + return TRUE; +} + +/** + * @brief getPWM - get PWM value for given pin on given port + * @param port + * @param pin + * @return -1 if there's no PWM on that pin + */ +int16_t getPWM(uint8_t port, uint8_t pin){ + timidx_t idx = timer_map[port][pin].timidx; + if(idx == TIM_UNSUPPORTED) return -1; + volatile uint32_t *CCR = &timers[idx]->CCR1 + timer_map[port][pin].chidx; + return (int16_t) *CCR; } diff --git a/F0:F030,F042,F072/usbcan_gpio/pwm.h b/F0:F030,F042,F072/usbcan_gpio/pwm.h index 1a04315..7fc5218 100644 --- a/F0:F030,F042,F072/usbcan_gpio/pwm.h +++ b/F0:F030,F042,F072/usbcan_gpio/pwm.h @@ -18,4 +18,32 @@ #pragma once +#include + +// used timers +typedef enum{ + TIM_UNSUPPORTED = 0, + TIM1_IDX, + TIM2_IDX, + TIM3_IDX, + TIM14_IDX, + TIM16_IDX, + TIMERS_AMOUNT +}timidx_t; + +// Timers for PWM +typedef struct{ + timidx_t timidx : 3; // timer index from array of timers used + uint8_t chidx : 2; // channel index (0..3) + uint8_t collision : 1; // have collision with other channel (1) + uint8_t collport : 1; // collision port index (0 - GPIOA, 1 - GPIOB) + uint8_t collpin : 4; // collision pin index (0..15) +} pwmtimer_t; + + void pwm_setup(); +int canPWM(uint8_t port, uint8_t pin, pwmtimer_t *t); +int startPWM(uint8_t port, uint8_t pin); +void stopPWM(uint8_t port, uint8_t pin); +int setPWM(uint8_t port, uint8_t pin, uint8_t val); +int16_t getPWM(uint8_t port, uint8_t pin); diff --git a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin index 25457cc09da79fa5c0b9657bc89d1fda25f8d4ff..b58d71185b03451f489b2be6a5bb16226abafc5b 100755 GIT binary patch delta 13574 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 delta 12670 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 diff --git a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user index 765e6fb..efeb3e1 100644 --- a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user +++ b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/F0:F030,F042,F072/usbcan_gpio/version.inc b/F0:F030,F042,F072/usbcan_gpio/version.inc index 951c9c1..34e7d71 100644 --- a/F0:F030,F042,F072/usbcan_gpio/version.inc +++ b/F0:F030,F042,F072/usbcan_gpio/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "192" +#define BUILD_NUMBER "207" #define BUILD_DATE "2026-03-15"