Skip to content

Commit eeb3b37

Browse files
author
Richard Unger
committed
added STM32SpeedDirCommander
1 parent d47243d commit eeb3b37

File tree

7 files changed

+338
-10
lines changed

7 files changed

+338
-10
lines changed

README.md

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,11 @@ The intent is to keep the core of SimpleFOC clean, and thus easy to maintain, un
1010

1111
## New Release
1212

13-
v1.0.3 - Released March 2023, for Simple FOC 2.3.0
14-
15-
What's changed since 1.0.2?
16-
- New Sensor: MT6835
17-
- Fixed bugs
18-
19-
What's changed since 1.0.1?
20-
- Calibrated sensor by @MarethyuPrefect
21-
- New Sensors: MT6701, MA330, MT6816
22-
- Fixed bugs
13+
v1.0.4 - Released May 2023, for Simple FOC 2.3.0
2314

15+
What's changed since 1.0.3?
16+
- New Input: STM32SpeedDirCommander
17+
- Fixed MT6835 driver bugs
2418

2519
## What is included
2620

@@ -49,6 +43,11 @@ What is here? See the sections below. Each driver or function should come with i
4943
### Communications
5044

5145
- [I2CCommander I2C driver](src/comms/i2c/) - I2C communications protocol and drivers for both controller and target devices.
46+
- [STM32 SpeedDir Input](src/comms/stm32speeddir/) - Control target velocity with PWM speed and direction inputs
47+
48+
### Utilities
49+
50+
- [STM32 PWM Input driver](src/utilities/stm32pwm/) - PWM Input driver for STM32 MCUs. Accurately measure PWM inputs with zero MCU overhead.
5251

5352

5453
## How to use
@@ -74,3 +73,14 @@ If you do so, please be sure to adhere to and include the [LICENSE](https://gith
7473
## Further Documentation
7574

7675
Find out more information about the Arduino SimpleFOC project on the [docs website](https://docs.simplefoc.com/)
76+
77+
## Release History
78+
79+
What's changed since 1.0.2?
80+
- New Sensor: MT6835
81+
- Fixed bugs
82+
83+
What's changed since 1.0.1?
84+
- Calibrated sensor by @MarethyuPrefect
85+
- New Sensors: MT6701, MA330, MT6816
86+
- Fixed bugs

src/comms/stm32speeddir/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
# STM32SpeedDirInput
3+
4+
Control SimpleFOC using PWM speed and direction inputs.
5+
6+
Based on STM32 timer PWM-Input capabilities, which means this can only be used on STM32 MCUs. It can cover a wide range of PWM frequencies, and runs without MCU overhead in the timer hardware.
7+
8+
9+
10+
## Setup
11+
12+
The PWM speed input should be connected to either channel 1 or channel 2 of a general purpose or advanced control timer on your STM32 MCU. Suitable timers are:
13+
- Advanced control timers: TIM1, TIM8
14+
- General purpose timers (32 bit): TIM2, TIM5
15+
- General purpose timers (16 bit): TIM3, TIM4, TIM9, TIM12
16+
If in doubt, check in STM32CubeIDE and see if the PWM-Input mode can be enabled (under "Combined Channels") for the timer.
17+
18+
The optional direction input can be connected to any GPIO pin. By default a high level direction input is associated with a positive velocity value, while a low level direction input results in a negative velocity value. To reverse this, set the option `dir_positive_high = false`
19+
20+
The direction input is optional - if not provided, you can control the direction from software using the `direction` field.
21+
22+
The velocity values returned are in the range `min_speed` to `max_speed`, while the input PWM duty cycle should lie within the range `min_pwm` to `max_pwm`. Actual input values smaller than `min_pwm` will be treated as `min_pwm`, values larger than `max_pwm` will be treated as `max_pwm`. The behaviour for 100% or 0% duty cycles is undefined, and they should be avoided.
23+
24+
## Usage
25+
26+
Use it like this:
27+
28+
```c++
29+
#include "comms/stm32speeddir/STM32SpeedDirInput.h"
30+
31+
// some example pins - the speed pin has to be on channel 1 or 2 of a timer
32+
#define PIN_SPEED PC6
33+
#define PIN_DIRECTION PB8
34+
35+
STM32SpeedDirInput speed_dir = STM32SpeedDirInput(PIN_SPEED, PIN_DIRECTION);
36+
37+
float target = 0.0f;
38+
39+
void setup(){
40+
...
41+
42+
speed_dir.min_speed = 10.0f; // 10rad/s min speed
43+
speed_dir.max_speed = 100.0f; // 100rad/s max speed
44+
speed_dir.min_pwm = 5.0f; // 5% min duty cycle
45+
speed_dir.max_pwm = 95.0f; // 95% max duty cycle
46+
speed_dir.init();
47+
48+
...
49+
}
50+
51+
52+
void loop(){
53+
target = speed_dir.getTargetVelocity();
54+
motor.move(target);
55+
motor.loopFOC();
56+
}
57+
58+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
#include "./STM32SpeedDirInput.h"
3+
4+
#if defined(_STM32_DEF_)
5+
6+
STM32SpeedDirInput::STM32SpeedDirInput(int pin_speed, int pin_dir) : STM32PWMInput(pin_speed) {
7+
_pin_speed = pin_speed;
8+
_pin_dir = pin_dir;
9+
};
10+
11+
STM32SpeedDirInput::~STM32SpeedDirInput(){};
12+
13+
int STM32SpeedDirInput::init(){
14+
pinMode(_pin_dir, INPUT);
15+
return STM32PWMInput::init();
16+
};
17+
18+
19+
float STM32SpeedDirInput::getTargetVelocity(){
20+
if (_pin_dir != NOT_SET)
21+
direction = digitalRead(_pin_dir);
22+
float speed = getDutyCyclePercent();
23+
speed = constrain(speed, min_pwm, max_pwm);
24+
speed = (speed - min_pwm)/(max_pwm - min_pwm) * (max_speed - min_speed) + min_speed;
25+
return (direction == dir_positive_high) ? speed : -speed;
26+
};
27+
28+
29+
#endif
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
#pragma once
3+
4+
#include "Arduino.h"
5+
6+
#if defined(_STM32_DEF_)
7+
8+
#include "common/foc_utils.h"
9+
#include "utilities/stm32pwm/STM32PWMInput.h"
10+
11+
class STM32SpeedDirInput : public STM32PWMInput {
12+
public:
13+
STM32SpeedDirInput(int pin_speed, int pin_dir = NOT_SET);
14+
~STM32SpeedDirInput();
15+
16+
int init();
17+
float getTargetVelocity();
18+
19+
float min_speed = 0; // min speed in rad/s
20+
float max_speed = 100; // max speed in rad/s
21+
float min_pwm = 5.0; // min duty cycle in %
22+
float max_pwm = 95.0; // max duty cycle in %
23+
bool dir_positive_high = true; // true if the direction pin is high when the motor is spinning in the positive direction
24+
bool direction = true; // direction of rotation, default positive
25+
protected:
26+
int _pin_speed;
27+
int _pin_dir;
28+
};
29+
30+
31+
#endif

src/utilities/stm32pwm/README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
2+
# STM32 Utilities - PWM Input
3+
4+
Using the `STM32PWMInput` class you can precisely read a PWM input signal on STM32 MCUs by using their timer's PWM-Input mode. This happens entirely in the timer hardware with zero MCU overhead, and is very precise.
5+
6+
## Setup
7+
8+
Connect the PWM input to either channel 1 or channel 2 of a general purpose or advanced control timer supporting PWM-Input mode.
9+
10+
This should include the following timers:
11+
12+
- Advanced control timers: TIM1, TIM8
13+
- General purpose timers (32 bit): TIM2, TIM5
14+
- General purpose timers (16 bit): TIM3, TIM4, TIM9, TIM12
15+
16+
The best to use are the 32 bit general purpose timers, although TIM1 may also be interesting because on some MCUs it can be clocked at a higher rate than the other timers.
17+
18+
## Usage
19+
20+
```
21+
STM32PWMInput pwmInput = STM32PWMInput(PA15);
22+
pwmInput.init();
23+
24+
uint32_t periodTicks = pwmInput.getPeriodTicks();
25+
uint32_t dutyTicks = pwmInput.getDutyCycleTicks();
26+
float dutyPercent = pwmInput.getDutyCyclePercent();
27+
```
28+
29+
## Input signal
30+
31+
The input signal should be a PWM signal (square wave) with a duty cycle that is >0% and <100%. The
32+
33+
The behaviour if the input is not a square wave is not defined, although the MCU will continue to sample the input, and resumes correct measurement when the square wave input is restored.
34+
35+
## Performance
36+
37+
The range of PWM frequencies that can be measured and the resolution with which they can be sampled depends on the following:
38+
39+
- timer clock speed - normally this is your MCU speed, but it can be lower, or in some cases even higher.
40+
- timer prescaler - can divide the timer clock.
41+
42+
The sample resolution for a PWM input signal of frequency _Fp_ is given by
43+
44+
_R = Fc / Fp_
45+
46+
Where _R_ is the resolution in ticks, and _Fc_ is the timer tick frequency (timer clock with prescaler).
47+
48+
This sample resolution is equal to the length of the PWM period in ticks. The timer needs to sample both the duty cycle and the full period, so the timer can't sample signals whose period would cause it to overflow.
49+
50+
On a 16 bit timer this means the period (and sample resolution) must be less than 65536 ticks, while on a 32 bit timer it must be less than 4294967296 ticks. This places a lower limit on the PWM frequencies that can be sampled.
51+
52+
On the other side, the minimum duty cycle that can be reliably captured is limited by the duration of one timer tick, e.g. the minimum on-time that can be measured reliably is equal to two timer ticks in duration.
53+
54+
So as the frequency increases, the resolution decreases, and the minimum duty cycle increases.
55+
56+
For example, on a 64MHz STM32 MCU, using TIM3 (16 bit) you could capture a PWM signal at 10kHz with a resolution of 6400 ticks. This fits in the 16 bit timer, so no problem. Assuming the signal is perfectly stable (it usually isn't) that would be 12 bits of accuracy on your input.
57+
58+
If you lowered the PWM input frequency to 1kHz, you'd get a 64000 range - just fitting in the timer. But now the accuracy is greatly increased - almost 16 bits!
59+
60+
If you increased the PWM frequency to 10Mhz, the resolution would be just 6 ticks, and the resulting accuracy very low - just 2 bits, with a minimum duty cycle of 33% and a maximum of 66%.
61+
62+
Of course you could vary the 10MHz signal faster than the 1kHz one - so choosing the PWM input frequency is a tradeoff between accuracy and control bandwidth...
63+
64+
Note: clock configuration is out of scope for this class. Set your clocks up in advance.
65+
66+
67+
68+
## Roadmap
69+
70+
- Support setting of the filtering function, this could help a lot against noise on the input
71+
- Support setting of the timer prescaler (not the clock prescaler!)
72+
- Support setting of the downsample function, this could help increase accuracy
73+
- Support choosing of the alternate timers on pins with more than one
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
2+
#include "./STM32PWMInput.h"
3+
4+
#if defined(_STM32_DEF_)
5+
6+
7+
8+
STM32PWMInput::STM32PWMInput(int pin){
9+
_pin = pin;
10+
};
11+
12+
13+
STM32PWMInput::~STM32PWMInput(){};
14+
15+
16+
17+
18+
19+
int STM32PWMInput::init(){
20+
PinName pinN = digitalPinToPinName(_pin);
21+
pinmap_pinout(pinN, PinMap_TIM);
22+
uint32_t channel = STM_PIN_CHANNEL(pinmap_function(pinN, PinMap_TIM));
23+
timer.Instance = (TIM_TypeDef *)pinmap_peripheral(pinN, PinMap_TIM);
24+
timer.Init.Prescaler = 0;
25+
timer.Init.CounterMode = TIM_COUNTERMODE_UP;
26+
timer.Init.Period = 4.294967295E9; // TODO max period, depends on which timer is used - 32 bits or 16 bits
27+
timer.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
28+
timer.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
29+
if (HAL_TIM_Base_Init(&timer) != HAL_OK) {
30+
return -1;
31+
}
32+
TIM_ClockConfigTypeDef clkCfg = { .ClockSource=TIM_CLOCKSOURCE_INTERNAL };
33+
if (HAL_TIM_ConfigClockSource(&timer, &clkCfg) != HAL_OK) {
34+
return -2;
35+
}
36+
if (HAL_TIM_IC_Init(&timer) != HAL_OK) {
37+
return -3;
38+
}
39+
40+
TIM_SlaveConfigTypeDef slCfg = {
41+
.SlaveMode = TIM_SLAVEMODE_RESET,
42+
.InputTrigger = (channel==1)?TIM_TS_TI1FP1:TIM_TS_TI2FP2,
43+
.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING,
44+
.TriggerPrescaler = TIM_ICPSC_DIV1,
45+
.TriggerFilter = 0
46+
};
47+
if (HAL_TIM_SlaveConfigSynchro(&timer, &slCfg) != HAL_OK) {
48+
return -4;
49+
}
50+
TIM_IC_InitTypeDef icCfg = {
51+
.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING,
52+
.ICSelection = (channel==1)?TIM_ICSELECTION_DIRECTTI:TIM_ICSELECTION_INDIRECTTI,
53+
.ICPrescaler = TIM_ICPSC_DIV1,
54+
.ICFilter = 0
55+
};
56+
if (HAL_TIM_IC_ConfigChannel(&timer, &icCfg, TIM_CHANNEL_1) != HAL_OK) {
57+
return -5;
58+
}
59+
icCfg.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
60+
icCfg.ICSelection = (channel==1)?TIM_ICSELECTION_INDIRECTTI:TIM_ICSELECTION_DIRECTTI;
61+
if (HAL_TIM_IC_ConfigChannel(&timer, &icCfg, TIM_CHANNEL_2) != HAL_OK) {
62+
return -6;
63+
}
64+
TIM_MasterConfigTypeDef mCfg = {
65+
.MasterOutputTrigger = TIM_TRGO_RESET,
66+
.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE
67+
};
68+
if (HAL_TIMEx_MasterConfigSynchronization(&timer, &mCfg) != HAL_OK) {
69+
return -7;
70+
}
71+
if (HAL_TIM_IC_Start(&timer, TIM_CHANNEL_1)!=HAL_OK) {
72+
return -8;
73+
}
74+
if (HAL_TIM_IC_Start(&timer, TIM_CHANNEL_2)!=HAL_OK) {
75+
return -9;
76+
}
77+
timer.Instance->CR1 |= TIM_CR1_CEN;
78+
return 0;
79+
};
80+
81+
82+
float STM32PWMInput::getDutyCyclePercent(){
83+
uint32_t period = getPeriodTicks();
84+
if (period<1) return 0.0f;
85+
return getDutyCycleTicks() / (float)period * 100.0f;
86+
};
87+
88+
89+
uint32_t STM32PWMInput::getDutyCycleTicks(){
90+
return timer.Instance->CCR2;
91+
};
92+
93+
94+
uint32_t STM32PWMInput::getPeriodTicks(){
95+
return timer.Instance->CCR1;
96+
};
97+
98+
99+
100+
#endif
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
3+
#include "Arduino.h"
4+
5+
#if defined(_STM32_DEF_)
6+
7+
8+
class STM32PWMInput {
9+
public:
10+
STM32PWMInput(int pin);
11+
~STM32PWMInput();
12+
13+
int init();
14+
15+
float getDutyCyclePercent();
16+
uint32_t getDutyCycleTicks();
17+
uint32_t getPeriodTicks();
18+
protected:
19+
int _pin;
20+
TIM_HandleTypeDef timer;
21+
};
22+
23+
24+
25+
26+
27+
#endif

0 commit comments

Comments
 (0)