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 25457cc..b58d711 100755 Binary files a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin and b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin differ 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"