Power monitoring on a three phase system with Arduino Uno - arduino

Hello
I am currently working on a project, where I want to measure the voltage and current in a 3-phase system with an Arduino Uno.
This is a small schoolproject and I've had the necessary course on AC-systems to know about safety around higher voltages. I've also have a little bit experience with microcontroller but I've never used ADC.
I have a problem when reading from the analog pins of the Arduino Uno. It seems like the analog pins are mixed which i believe is called ghosting. I've been searching the internet for some answers to this matter, but the proposed solutions didn't work for me. I tried to make a dummy measurement and also to make a small time delay between measurements but since it's about power monitoring timing is critical. I need at minimum 20 readings which needs to be done in 20ms
To test the code I used two function generators. Is this even possible or allowed? Is it best to have at minimum a resistance in between and maybe a capacitor to remove noise?
Is there something in the circuit when transforming the voltage/current to be between 0V-5V there can be done to prevent this ghosting-effect?
I am using a voltagetransformer for the voltage and a Hall-effect sensor for the current. Both circuits need offset.
This is the code that makes the measurements.
void measure(char pin_volt, char pin_curr, int *volt_rms, int *curr_rms, float *theta){
int i;
long squared_v, squared_c, sum_squared_v = 0, sum_squared_c = 0, inst_v, inst_c, mean_squared_v, mean_squared_c;
unsigned long time_v, time_c;
for(i = 0; i < samples; i++){
inst_v = analogRead(pin_volt) - volt_offset;
if(inst_v > -volt_varying && inst_v < volt_varying) {
time_v = micros();
}
inst_c = analogRead(pin_curr) - curr_offset;
if(inst_c >= -curr_varying && inst_c <= curr_varying) {
time_c = micros();
}
squared_v = inst_v * inst_v;
squared_c = inst_c * inst_c;
sum_squared_v += squared_v;
sum_squared_c += squared_c;
delayMicroseconds(80);
}
mean_squared_v = sum_squared_v / samples;
mean_squared_c = sum_squared_c / samples;
*volt_rms = sqrt(mean_squared_v);
*curr_rms = sqrt(mean_squared_c);
*theta = calculate_phase_difference(time_v,time_c);
}

Adding a capacitor can lower the problem.
Try to do the following:
No current or tension on the circuit, so the arduino should measure 0 values.
Run a sketch that reads values and prints max and min values to serial monitor; you will see that values will not be zero as expected, those are interferences.
Try and find a capacitor that can lower those values but don't exagerate.

Related

How to control the speed of a stepper motor using PID control and ultrasonic sensor?

Little backstory:
I'm currently doing this project that deals with using two cars, called block A and B, which block B has to maintain a distance of 10 cm from block A using PID, PD, PI, or P. I'm using a PID. Block B uses an Arduino whereas Block A is controlled by the user by hand. Block B uses a unipolar stepper motor as the actuator and an ultrasonic sensor to sense the distance. My professor wants the motor to move in both directions and have varying speeds (slow, medium, and fast). I'm using brett's PID since I have used it before in my previous labs.
Problem:
I have an issue with how to create varying speeds for block B like intuitively I know that I want the B should move for example, fast if the car is greater than 20 cm, medium if the car is between 20cm and 14cm, and slow if it's between 14cm and 10cm. But I just can't use the input value retrieved from the sensor directly to control the motor as it would make it an open system. So I used the error retrieved from Brett's PID code to control the stepper motor. So far, I have gotten the directions to work by setting the myPID.SetOutputLimits(-800,800);. But as it tried to use the error to control the speed it would be impossible because the error always fluctuates at a specific distance. For example at 12cm, I would get either 800 or around 300. I'm currently confused about how to implement control of the speed of my stepper motor through PID and any help regarding this issue will be appreciated.
Code:
Code was through Arduino IDE.
#include "SR04.h"
#include <Stepper.h>
#include <PID_v1.h>
#define TRIG_PIN 7
#define ECHO_PIN 6
//intialization of Ultrasonic sensor
SR04 sr04 = SR04(ECHO_PIN,TRIG_PIN);
long s;
//intializing motor variables
int stepsPerRevolution = 2048;
int motorSpeed = 6;
Stepper myStepper (stepsPerRevolution, 8, 10, 9, 11);
//Declared PID variables
double Setpoint = 10; //desired temp value
double Input; //thermsitor
double Output; //DC motor
double Error;
//defined variables for PID parameters
double Kp=100, Ki=10, Kd=1;
//PID equation
PID myPID(&Input, &Output, &Setpoint, Kp, Kd, Ki, REVERSE);
void setup(){
Serial.begin(9600);
//setting PID
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-800,800);
//speed intialized
myStepper.setSpeed(motorSpeed);
}
void loop(){
s=sr04.Distance();
Input = s;
myPID.Compute();
Error = Input - Setpoint;
//Serial.print(Input);
//Serial.print(",");
//Serial.println(Setpoint);
Serial.println(Output);
//Serial.print(",");
//Serial.println(Error);
Error = Output;
//Away from Block B
if (0<Error<800){
myStepper.setSpeed(motorSpeed);
myStepper.step(-300);
} //slow speed
if (Error>=800){
myStepper.setSpeed(motorSpeed*2);
myStepper.step(-128);
} //fast speed
//Towards Block B
if (-800<Error<0) {
myStepper.setSpeed(motorSpeed);
myStepper.step(128);
} //slow speed
if (Error<=-800) {
myStepper.setSpeed(motorSpeed*2);
myStepper.step(128);
}//Fast speed
}
What you need to do is calcuulate how much you need to change your current speed to minimize the error in distance.
Your calculation for error is not in the right place.
void loop()
{
long s=sr04.Distance();
Input = s; // using global variables to pass values to your PID
// is not a good idea. Use function parameters instead.
// You are storing a 32 bit value in a 16 bit variable!!!
// That's only the start of your problems.
myPID.Compute();
Error = Input - Setpoint; //
Since we're starting with a major design flaw, I'll have to assume you'll fix that and change your PID code to accept and compute long integers both as input value as a function parameter, and as the type of its return value..
What you want to do is compute the PID from the error in distance from your set point, and then modulate the current speed accordingly. PIDs work best when used directly, using 7 speeds (1 stopped, 3 forward/3 backwards) is possible, but I don't think it'll give better results, I'll leave the exercise to you.
I haven't tried this, I don't have any cars on hand. This is a skeletoon of how I'd go about it. Tuning the PID should be what takes you the longest.
//...
// speeds are in RPMs.
long curSpeed = 0;
const long MAX_SPEED = XXX; // whatever you max speed is for your car.
const long MIN_NEG_SPEED = -XXX; // whatever you max speed is for your car going reverse.
const long MIN_SPEED = XXX; // below this absolute speed, we're stopped.
const int SLICE_TIME = 10; // time between readings and adjustments, in ms.
// you'll need to adjust this according to you minimum speed, and steps per turn.
const long STEPS_PER_TURN = 200; // change to whatever you steps/turn value is.
// you'll need to limit the output of your PID to match the acceleration your
// motors can handle for your particular car.
// returns the number of steps to run for our slice time.
long Steps(int speed)
{
if (-MIN_SPEED <= speed && speed <= MIN_SPEED)
return 0;
// compute number of steps for our slice time.
// choose slice time and minimum speed wisely!!
long steps = (SLICE_TIME * (speed * STEPS_PER_TURN)) / (60000L);
// for very low speeds. I've added this, because I'm unsure of the
// time domain behaviour of stepper library with less than 2 steps
if (-1 <= steps && steps <= 1)
{
if (speed < 0)
return -2;
else
return 2;
}
return int(steps);
}
void loop()
{
// You may want to filter the sr04 readings with a median of 5
// filter to limit input noise.
// You want to keep the car at a distance of 'set_point'
// from the leading car. distance_error is the error you want to
// minimize to zero by using the PID, and that's what should be
// the PID input.
//
// The way this works. We are rolling at speed curSpeed, we
// measure the error in distance from our set_point, feed that
// to the PID, then accelerate or decelerate by subtracting
// the output of the PID from the current speed.
//
// Note: you can add or subtract the PID to/from the current speed,
// the sign of the PID depends on you coefficients and sensor.
// I've used subtraction here because that's how you express
// negative feedback mathematically. In real life, we'll use what
// best fits our needs. Usually it's the operation that makes P
// positive.
long distance_error = sr04.Distance() - setPoint;
long pid_out = myPID.Compute(distance_error);
// increment or decrement the current speed to try and reduce the error.
long speed = curSpeed - pid_out; // As usual, PID coefficients do matter
// for this to work well.
if (speed > MAX_SPEED)
speed = MAX_SPEED;
if (speed < MIN_NEG_SPEED)
speed = MIN_NEG_SPEED;
curSpeed = speed;
if (speed < 0)
speed = -speed;
myStepper.setSpeed(speed); // modulate speed
int steps = Steps(curSpeed);
if (steps)
myStepper.step(steps); // keep rolling.
}
I haven't tried to compile it either, so this may not compile as is. But most of the tricks and traps are covered, and this should give you a head start, if you want to go the PID route. But I think your professor will really wonder where that one came from :) Still, you should try and make it run, for fun.
The other way, without a PID, and using set speeds is much more straightforward. It may also be closer to what the is required by the exercise. The distance between cars will vary a bit more, of course. And it does not use a PID at all.
const int MAX_SPEED = 3;
int speed = 0; // value range is [-MAX_SPEED, +MAX_SPEED]
long RPMS[MAX_SPEED + 1] = { 0, 200, 400, 800 }; // in RPMs, assuming average speed will be around 400, in this case.
// For 3 speeds, the difference between speeds cannot be higher than max acceleration.
// You can add as many speeds as desired. More speeds = more precision.
const long STEPS_PER_TURN = 200; // change to whatever you steps/turn value is. MUST be 200 or more.
const int STEPS = STEPS_PER_TURN / 100; // 3.6° between speed adjustment.
// it is very small right now, so
// you will want to play with this value.
// this threshold gives some control over aceleration.
// and 'hardness' of distance tracking.
const long THRESHOLD = 0;
void loop()
{
// get the error in distance.
long distance_error = sr04.Distance() - setPoint;
// modulate speed.
if (distance_error > THRESHOLD)
++speed;
if (distance_error < -THRESHOLD)
--speed;
if (speed > MAX_SPEED)
speed = MAX_SPEED;
if (speed < -MAX_SPEED)
speed = -MAX_SPEED;
long rpm = RPMS[(speed < 0) : -speed : speed];
if (rpm)
{
myStepper.setSpeed(rpm);
myStepper.setSpeed((speed < 0) ? -STEPS : STEPS)
}
}
For this code, you must choose speeds and STEPS value that will give you an acceleration without misssed steps.

To know the % charge of the battery using Arduino

I am using Arduino Nano and various Li-Fe , Li-Po batteries of 9.9V , 6.6V and 3.7V.
I can read the voltage of the battery using Arduino . My Arduino works at 5V so for batteries like 9.9V and 6.6V I have used a voltage divider using two 10k resistors.But the problem is I need to read the the % of charged battery , I tried something in the code but I am not sure about it. Please anyone help me with it.
My code is:
#define cellPin A0
const float mvpc = 4.55 ; //measured voltage of arduino through voltmeter
float counts = 0; //battery volts in millivolts
float mv = 0;
float multiplier = 2;
float output = 0;
int charge = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
}
void loop() {
// put your main code here, to run repeatedly:
counts = analogRead(cellPin);
Serial.println(counts);
mv = counts * mvpc;
Serial.println(mv);
output = (mv * multiplier)/1000 ;
Serial.print(output);
Serial.println("V");
charge = (counts/1024)*100;
Serial.print(charge);
Serial.println("%");
delay(1000);
}
In order to accurately determine the % of the charged battery, you need the discharge graph for each of the batteries. The discharge graph is usually non-linear for lithium batteries. The discharge curve is basically the voltage versus the % charge and it is different for charging and discharging batteries.
If you have the discharge curve, you can create a map for each of the % to the corresponding voltage value. Then you can map each voltage to a % value from the map you created.
For example:
100% -> 5.00 V
99% -> 4.95 V
....
0% -> 3.23 V
Create an array to store the map of size 100 (for each %): [5.00, 4.95, ... 3.23]
You can then find the %s using the voltage. I hope you can find the discharge graph, otherwise, you can manually find it yourself by discharging the battery using a safe current
In addition to maheenul I'd like to add that you're calculations are a bit off.
const float mvpc = 4.55 ; //measured voltage of arduino through voltmeter
I presume you're supplying your Arduino via USB. This voltage is not very reliable.
float counts = 0; //battery volts in millivolts
counts = analogRead(cellPin);
analogRead returns a 10bit (0-1023) ADC reading. A fraction of your voltage reference. It is not a value in millivolts! So in the following you shoudl use 1024 instead of 1000.
output = (mv * multiplier)/1000 ;
charge = (counts/1024)*100; this calculates the percentage of your Vref. It is not a charge as explained by maheenul.
If you want accurate measurements you should use a better Vref. Either the internal 1.1Vref or some well regulated Vref. Both with appropriate voltage dividers.
But I guess being 5-10% off is not a big deal for a battery charge measurement.

Calculating wind speed with pitot tube using arduino

Trying to calculate the wind speed using a pitot tube MPXV7002DP.
We are getting the dynamic pressure from the sensor and then applying Bernoulli's equation:
pd = dynamic pressure
air density = 1.225kg/m^3
windspeed = sqrt(2*Pd/air density)
We are using an Arduino UNO.
We think that there is problem with reading the pressure from the sensor.
We don't know how to get the correct values.
#include <SoftwareSerial.h>
float Output=0;
void setup() {
Serial.begin(9600);
}
void loop() {
float sensorValue = analogRead(A0);
output=sqrt((2*sensorValue)/1.225);
Serial.println(output);
Serial.print("m/s");
delay(100);
}
As one commenter pointed out, the return value of analogRead is an integer from 0-1023. This is a scaling of the voltage on pin A0, from 0 to the comparison voltage. (If you're using a 5V Arduino, 1023 is 5V. 3V Arduino, 1023 is 3V. It doesn't look like you're doing anything complicated that alters what you're using as your comparison voltage, so this should be accurate.)
I'm going to assume that you're working with 5V, since that's what your sensor uses.
What you need to do is look at the data sheet for your device, to determine what the relationship between pressure and voltage is. Looking at the sheet, you have a complicated bugger here, but from the graph on page 5, it looks like you can assume that 0.5V (analogRead of around 102) is a pressure of -2kPa and a 4.5V (analogRead of around 921) is a pressure of 2kPa. You're in luck that the scaling is linear.
I say "around" because it's clear that the device has quite a bit of slop in it's response - at least plus-or-minus .5V or .2kPa! (In other words, 0kPa could read anywhere from 462 to 562.)
You should map analogRead values from 0-102 to -2kPa, from 921-1023 to 2kPa, and everything in between should be a lerp between -2 and 2. I don't have an arduino in front of me so I can't try it out, but it should be something like:
result = analogRead(A0);
if (result < 102) {
kPa = -2.0;
} else {
if (result > 921) {
kPa = 2.0;
} else {
kPa = map(result, 102, 921, -2000, 2000)/1000.0;
}
}
Let me know, in the comments, if I screwed something up and I'll see about fixing it. This is all without the benefit of being able to actually compile/test. =]
When you mention the dynamic pressure pd in Bernoulli' equation I suppose you mean the difference between the totalpressure and the static pressure, because the airspeed V is equal to : V = sqrt(2 * (p_total - p_static) / airdensity), so your pd should be (p_dynamic - p_static).

LM35 decreases when near heat

My LM35 connected to arduino decreases temperature value in celsius when near heat and increases value when far from heat. Could any one help or know why it's working other way round.
void setup() {
// put your setup code here, to run once:
//Start the serial connection with the computer
//to view the result open the serial monitor
// 9600 is the “baud rate”, or communications speed.
Serial.begin(9600);
}
void loop() {
delay(2000);
float tempValue = analogRead(A2);
// converting that reading to voltage
float tempVoltage = (tempValue/1024.0)*5.0;
float tempDegrees = (tempVoltage - 0.5) * 100.0 ;
//Multiplying tempDegrees by -1 to make it positive
tempDegrees =(tempDegrees * -1);
Serial.println("............................................");
Serial.println("Degrees");
Serial.println(tempDegrees);
delay(2000);
}
just randomly came accross your question. and it has been 6 years since I touched an LM35 :d
but I think you have a problem in that -0.5 thing. I did not really get that!
LM35's function as far as I remember was :
T = V/ 10mV
you might want to check the datasheet but I'm pretty positive this is the equation. when you get the voltage from ADC you have to put it in this equation and get the result.
be careful : you have to also attribute for the temperature error as well as ADC noise if temperature precision is important for you.
If you are using a 5 volts power supply to your arduino:
5 Volts in the Arduino are directly converted to 1023 in the output of the ADC
ADC_Outpput * 5000 / 1024, where 5000 is coming from 5volts as millivolts, 1024 is the 10 bist resolution
LM35 resolution is linearly generated with a rate of + 10-mV/°C
so the analogVolt = ADC_Outpput * 5000 / 1024
FinalTemperature = (analogVolt - 500) / 10

STM32F4 Encoder count is changing when it should not

I am currently using the STM32F4 with the STM32F429ZI Nucleo-144 Board. I am looking to use this microcontroller to evaluate the position of a rotary encoder via a quadrature encoder interface. Looking at the documentation, this is done with the timers. I have the A/B encoder outputs hooked up to PA6 and PC7 on the micro, but I have noticed that the counts appear to be drifting.
During the debugging, I noticed that if I disconnect one of the encoder outputs to the microcontroller and I move the motor, the counts still increment/decrement even though only one of the encoder lines are connected. Since I am counting on both the TI1 and TI2 edges, this should not be happening. If I am reading the below diagram correctly, since one of my lines is held high using the internal pull-up, clock pulses on the other input should be going up/down/up/down and really just cycling between two different counts. However, if I am rotating the encoder, the counts keep incrementing or decrementing depending on the direction.
Why is the encoder count changing with only one encoder input connected? I also have the scope trace attached to prove that only one count is is active, as well as the code.
EDIT: I have also tried changing the polarity from BOTH EDGE to RISING EDGE, with no perceived benefit.
#include "stm32f4xx_hal.h"
#include "encoder_test.h"
GPIO_InitTypeDef GPIO_InitStruct;
TIM_HandleTypeDef Timer_InitStruct;
TIM_Encoder_InitTypeDef Encoder_InitStruct;
void EncoderTest_Init()
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
/**TIM3 GPIO Configuration
PA6 ------> TIM3_CH1
PC7 ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
Timer_InitStruct.Instance = TIM3;
Timer_InitStruct.Init.Period = 0xFFFF;
Timer_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
Timer_InitStruct.Init.Prescaler = 1;
Timer_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
Encoder_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12;
Encoder_InitStruct.IC1Filter = 0x00;
Encoder_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
Encoder_InitStruct.IC1Prescaler = TIM_ICPSC_DIV1;
Encoder_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI;
Encoder_InitStruct.IC2Filter = 0x00;
Encoder_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
Encoder_InitStruct.IC2Prescaler = TIM_ICPSC_DIV1;
Encoder_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI;
if (HAL_TIM_Encoder_Init(&Timer_InitStruct, &Encoder_InitStruct) != HAL_OK)
{
while (1);
}
if (HAL_TIM_Encoder_Start_IT(&Timer_InitStruct, TIM_CHANNEL_1) != HAL_OK)
{
while (1);
}
}
void TIM3_IRQHandler()
{
HAL_TIM_IRQHandler(&Timer_InitStruct);
}
Upon further investigation, it appears that the issue is due to the prescaler. The prescaler does not work in encoder mode when you provide even values. Since prescaler is the entered value + 1, using the STM32F4 HAL, the entered prescaler must be even.
I found confirmation that I am not the only person with this issue at this forum post. There is some discussion at the post that prescalers may not be compatible with encoder mode, but this has not yet been confirmed. I have sent an email to ST to get to the bottom of it. It is safe to enter a prescaler value of 0 if it is not supported.
Here is the working code below:
#include "stm32f4xx_hal.h"
#include "encoder_test.h"
GPIO_InitTypeDef GPIO_InitStruct;
TIM_HandleTypeDef Timer3_InitStruct;
TIM_Encoder_InitTypeDef EncoderTim3_InitStruct;
void EncoderTest_Init_Tim3()
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
/**TIM3 GPIO Configuration
PA6 ------> TIM3_CH1
PC7 ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
Timer3_InitStruct.Instance = TIM3;
Timer3_InitStruct.Init.Period = 0xFFFF;
Timer3_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
Timer3_InitStruct.Init.Prescaler = 10;
Timer3_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
EncoderTim3_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12;
EncoderTim3_InitStruct.IC1Filter = 0x00;
EncoderTim3_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
EncoderTim3_InitStruct.IC1Prescaler = TIM_ICPSC_DIV4;
EncoderTim3_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI;
EncoderTim3_InitStruct.IC2Filter = 0x00;
EncoderTim3_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
EncoderTim3_InitStruct.IC2Prescaler = TIM_ICPSC_DIV4;
EncoderTim3_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI;
if (HAL_TIM_Encoder_Init(&Timer3_InitStruct, &EncoderTim3_InitStruct) != HAL_OK)
{
while (1);
}
if (HAL_TIM_Encoder_Start_IT(&Timer3_InitStruct, TIM_CHANNEL_1) != HAL_OK)
{
while (1);
}
}
void TIM3_IRQHandler()
{
HAL_TIM_IRQHandler(&Timer3_InitStruct);
}
EDIT:
After speaking with ST tech support, the encoder interface was not intended to be used with a prescaler value, even OR odd. I have pasted their response below, but even with using a prescaler value that appears to work, it seems possible that the encoder counts drift over time.
Another solution is to use no prescalers, but instead extend the 16 bit value into the 32 bit space using the approach suggested here. I have reprinted the approach here in case the link goes dead:
From user goosen.kobus.001 on 11/19/2013 on ST's forum:
In my experience using an overflow interrupt to scale up an encoder is not reliable, especially when you have high resolution encoders: it happens from time to time that the encoder value will change sign in the instant you enter the interrupt, causing the system to increment the upper word when it should have decremented etc. This is especially true if the encoder is supposed to be stalled at 0, like a servo motor commanded to go to encoder position 0.
The best approach I have found is to do it manually. this is my procedure:
Ensure that the control loop that reads the encoder value is run often, (i.e. that if your encoder is rotating at full speed, the encoder value is still read at least 10-20 times between overflows. For my servo motor application a 1ms loop interval was sufficient.
keep track of the last read encoder value.
divide the current and last encoder value into quadrants (the most significant 2 bits). i.e. pos_now &= 0xC000; pos_last &= 0xC000;
check to see if the encoder has moved from quadrant 0 to quadrant 3 or 3 to 0 in the last step:
4.1 if(pos_now == 0 && pos_last == 0xC000) upper_word++;
4.2 if(pos_now == 0xC000 && pos_last == 0) upper_word--;
this is why I say the encoder read loop needs to be run often; you need to be sure that the value is read often enough that it is impossible to go from quadrant 0->1->2->3 in between reads.
It should also be possible to put this logic in another timer interrupt that runs at say 10kHz. that way you have an encoder value which is always up to date.
ST RESPONSE:
Hi,
I have got the feedback from the design and the architect of the timers.
The encoder interface has been designed to work without prescaler in order to not downgrade the resolution of the encoder.
As you observed, they have confirmed that it cannot work with even prescaler value but only the odd ones.
We have a sub-counter for the prescaler which is mono-directional, so not affected by the counter direction and incremented on every rising edge of the timer clock (without the prescaler).
The counter direction is updated on every rising edge of the timer clock (without the prescaler) but the counter is incremented only while the sub-counter of the prescaler reach the programmed value and according to the value in the direction bit.
So, in one case, the behavior is the same as without prescaler, because the counter is updated with a different direction (every odd clock cycles number), but in the other case, the direction is always the same when the counter is updated and the encoder interface doesn't work correctly.
So you can use the prescaler but with an odd value.
The recommended use case is without prescaler.
Best regards
ST MCU Tech Support

Resources