mirror of
https://github.com/eddyem/stm32samples.git
synced 2026-03-20 00:30:57 +03:00
PWM works
This commit is contained in:
@@ -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<<pin)) val = 1;
|
||||
else val = 0;
|
||||
break;
|
||||
break;
|
||||
case MODE_ANALOG:{
|
||||
int8_t chan = get_adc_channel(port, pin);
|
||||
if(chan >= 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -16,17 +16,160 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stm32f0.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -18,4 +18,32 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// 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);
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 18.0.2, 2026-03-15T02:24:04. -->
|
||||
<!-- Written by QtCreator 18.0.2, 2026-03-15T23:41:05. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#define BUILD_NUMBER "192"
|
||||
#define BUILD_NUMBER "207"
|
||||
#define BUILD_DATE "2026-03-15"
|
||||
|
||||
Reference in New Issue
Block a user