Issue with STM32F769 PWM output when updated according to condition - c++

I am using STM32F769 Disc board with Mbed online compiler. My task is to change the PWM frequency and duty ratio according to input.
I've created a simple algorithm according to my need, the program is working well but whenever the controller updates the PWM frequency, there is strange behavior(overlapped maybe, difficult to explain verbally for me), the frequency is changed instantly and the output is incorrect at that moment. For other controllers (like arduino) this never happens, the controller updates value after the time period of PWM is over. But not in this case.
What can be wrong?
I thought to add a small delay before value is updated but that will not work, as every time a different delay would be needed. I have attached the code and screenshots.
#include "mbed.h"
AnalogIn analog_value(A0);
PwmOut pulse(D11);
int main() {
double meas_v = 0;
double out_freq, out_duty, s_time;
while (1) {
meas_v = analog_value.read() * 3300;
if (meas_v < 1) {
out_freq = 50000;
out_duty = 40;
} else if (meas_v >= 1000) {
out_freq = 100000;
out_duty = 80;
} else {
out_freq = 50000 + (meas_v * 50);
out_duty = 40 + (meas_v * 0.04);
}
pulse.period(1.0 / out_freq);
pulse = out_duty / 100;
s_time = 0.0001;
wait(s_time);
}
}
The output should be updated after the current period is completed, not instantly.
Error I am getting

The underlying HAL code probably resets the current count value of the timer when you set the new period. You'll have to read the current timer cnt value and wait for it to reach 0. You can set a new period when the timer cnt value reaches 0.

You need to update the value in the update interrupt or use the timer DMA burst mode

Related

How to make DC motor's RPM come to its maximum value (analog 255) SLOWLY

It is a homework and I have completely NO idea, my teacher says you need just while, analogWrite and a counter. I have a DC motor, a transistor and a 9V battery.
I know my code does NOTHING, but just as example.
int analogPin = 3;
int count = 0;
void setup()
{
pinMode(analogPin, OUTPUT);
}
void loop() {
while(count<30){
analogWrite(analogPin , 255);
delay(20000);
count++;
}
}
You need to use counter value as your analogue output value:
void loop()
{
if( count < 256 )
{
analogWrite( analogPin, count ) ;
delay( 20000 );
count++ ;
}
}
Note that you do not need a while loop; the Arduino framework already calls loop() iteratively (the clue is in the name). However if your teacher thinks you need one and is expecting one, you may need to use one just for the marks. Alternatively discuss it with your teacher, and explain why it is unnecessary
In fact the delay too is arguably bad practice - it is unhelpful in applications where the loop() must do other things while controlling the motor. The following allows other code to run whilst controlling the motor:
unsigned long delay_start = 0 ;
void loop()
{
if( count < 256 &&
millis() - delay_start >= 20000ul )
{
analogWrite( analogPin, count ) ;
count++ ;
delay_start = millis() ;
}
// Do other stuff here
}
Because the loop() now never blocks on the delay() function, you can have code that does other things such as read switch inputs and it can react to them instantly, whereas as in your solution, such inputs could be ignored for up-to 20 seconds!
A typical DC motor will not start moving at very low values - you may want to start count somewhat higher than zero to account for the "dead-band". Analogue signals are also generally a poor way to drive a DC motor and varying speed; a PWM is generally a more efficient method, and will allow the motor to run at lower speeds. With an analogue signal at low levels (lower than for PWM), your motor will not move and will just get warm and drain your battery.
For test purposes, reduce the delay time; you don't want to sit there for an hour and 25 minutes just to find the code does not work! Set it to say 500ms, then start it, time how long it takes before the motor starts to move. If that is say 30 seconds, then yu know the motor starts to move when count is about 60; in which case that is a better starting value that zero. Then you can increase your delay back to 20 seconds if you wish - though a DC power supply might be better than a battery - I'm not sure it will last that long.

Accelerometer vibration detection using a threshold

I've program the Accelerometer to detect vibrations by setting a reasonable min/max threshold along all 3-axis' raw data. I need it to only count how many times it detects vibration, however, due to the way it's programmed with the threshold, I used a delay of about 1 sec in order to prevent multiple miscounts, which works but interferes with the Ultrasonic Module (HC-SR04) when it needs to read distance values is synchronously with the Accelerometer. Would like to get some feedback on this.
As far as I understand, you are using Arduino's delay() function. Bad idea as you block the all the rest of your application, well, you noticed already...
Better approach just checking if some time elapsed, e. g. using millis function:
static bool isDelay = false;
static unsigned long timestamp;
if(detect())
{
isDelay = true;
timestamp = millis();
}
if(isDelay && millis() - timestamp > 1000)
{
isDelay = false;
}
if(!isDelay)
{
// actions to be taken...
}
Always use subtraction between the timestamp and the current time – the time counter might overflow, subtraction result is unaffected, though, and you are safe...
You can simply skip the isDelay variable if you are sure enough that the relevant event always occurs at least once in between overflow period (around 50 days):
static unsigned long timestamp = millis() - 1000;
if(detect())
{
timestamp = millis();
}
if(millis() - timestamp > 1000)
{
// actions to be taken...
}
Both variants: static variables as assuming you have this code in Arduino's loop function (or one being called from loop). Yet prefer replacing the magic number 1000 with a macro and you're fine...

For loop with if statement for ardiuno powered humidity control

Really simple question but I'm not entirely sure how to incorporate a for loop in the if statement I have. Context: I have a humidifier I am trying to automate based on the humidity of the room. I'm using an ardiuno, dht11 humidity sensor and a servo. The humidifier knob has three settings (high low off) and so the servo has three positions. I have the code running so the servo turns appropriately according to the humidity level. The issue is that it fluctuates very easily. To correct that I'm looking to incorporate a for loop so that after let say 60 one second iterations of the humidity being greater than 55 the servo moves. I tried to add a for loop but it doesn't seem to be working.
But this is only my solution based on the little programming I know. If there is a better solution or even an equally viable alternative I'd love to know. I'm currently studying mechanical engineering but I'm finding that to really make something one needs a background in electronics and code. I'm trying to learn both independently through a series of projects and so I'm quite eager to learn. Hopefully this helps explain why I'm asking such a simple questions to begin with.
#include <dht.h>
#include <Servo.h>
Servo myservo;//create servo object to control a servo
dht DHT;
#define DHT11_PIN 7 // pin for humidity sensor ( also measure temp)
void setup() {
myservo.attach(9);//attachs the servo on pin 9 to servo object
myservo.write(0);//statting off position at 0 degrees
delay(1000);//wait for a second
Serial.begin(9600);
}
void loop() {
int chk = DHT.read11(DHT11_PIN); // the follow is just so that I can see the readings come out properly
Serial.print("Temperature = ");
Serial.println(DHT.temperature);
Serial.print("Humidity = ");
Serial.println(DHT.humidity);
delay(500);
if (DHT.humidity > 55) // here is where my code really begins
{
for (int i=0; i>60; i++); // my goal is to execute the follow code after the statement above has been true for 60 one second iterations
{
myservo.write(0);//goes to off position
delay(1000);//wait for a second
}
} else if (DHT.humidity > 40 ) {
for (int i=0; i>60; i++); // same thing here
myservo.write(90);//goes to low position
delay(1000);//wait for a second
}
else
{
for (int i=0; i>60; i++);
myservo.write(180);//goes to high position
delay(1000);
}
} // end of void loop()
Just addressing your question, the following line is incorrect:
for (int i=0; i>60; i++);
Two things:
1) The second statement in the for loop describes the conditions on which it executes. The way it is written, it will only execute when i>60 (not what you want according to the comments).
2) The semicolon after the for statement makes the next block unassociated.
Correct that line to the following:
for (int i=0; i<60; i++)
See the following for more information:
https://www.tutorialspoint.com/cprogramming/c_for_loop.htm
It would probably be helpful to examine your compiler warnings, and/or set a higher warning level, to catch these type of things early (this is, of course, somewhat compiler dependent).
I guess you trying kind of de-bouncing at you need humid level stay in same range for some period.
First, I define conversion function to map humid level to state
#define HUMID_OFF 1
#define HUMID_LOW 2
#define HUMID_HIGH 3
byte state_conv (float humid_level){
if (humid_level > 55) return HUMID_OFF ;
else if (humid_level > 40 ) return HUMID_LOW ;
else return HUMID_HIGH ;
}
Second I will check changing of state and use millis() to count time while current state is steady. if counting time are longer than threshold then change the actual state.
/*Global variable*/
byte actual_state;
byte flag_state;
void setup (){
// Do things that necessary
float humid = dht.readHumidity();
/*Initialize value*/
actual_state = state_conv(humid);
flag_state= state_conv(humid);
}
void loop(){
static unsigned long timer = millis();
float humid = dht.readHumidity();
byte crr_state = state_conv(humid);
if (crr_state != actual_state ){// if state is changing
if (flag_state != crr_state){
/if crr_state change form last iteration then reset timer
flag_state = crr_state;/
timer = millis();
}
else if (millis() - timer > 10000){
//if crr_state not change for 10000 ms (10 second)
actual_state = crr_state; // update actual state to crr_state
}
}
// After this use actual_state to control servo
if (actual_state == HUMID_OFF ){
myservo.write(0);//goes to off position
}
else if (actual_state == HUMID_LOW ){
myservo.write(90);//goes to low position
}
else if (actual_state == HUMID_HIGH ){
myservo.write(180);//goes to high position
}
}
DHT.humidity returns a float, so if you want to compare, then first store this in an int, and then compare.

Using 4 16bit timers for 400hz PWM

I'm dealing with arduino mega based quadcopter and trying to make PWM frequency for 4 motors - 400hz each. I've found an interesting solution where 4 ATmega2560 16bit timers are used to control 4 ESCs with PWM so it could reach 400hz frequency. 700 to 2000µs are normal pulse widths ESC are dealing with.
1sec/REFRESH_INTERVAL = 1/0.0025 = 400hz.
this is servo.h lib:
#define MIN_PULSE_WIDTH 700 // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH 2000 // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH 1000 // default pulse width when servo is attached
#define REFRESH_INTERVAL 2500 // minimum time to refresh servos in microseconds
#define SERVOS_PER_TIMER 1 // the maximum number of servos controlled by one timer
#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)
The problem is to make it work each PWM should be controlled with 1 16bit timer. Otherwize, say, 2 escs on 1 timer would give 200hz. So all of 16bit timers are busy controlling 4 ESC but I still need to read input PPM from receiver. To do so I need at least one more 16bit timer which I don't have anymore. It's still one 8bit timer free bit it can only read 0..255 numbers while normal number escs operate with are 1000..2000 and stuff.
So what would happen if I'll use same 16bit timer for both pwm and ppm reading? Would it work? Would it decrease speed drastically? I have arduino working in pair with Raspberry Pi which controls data filtering, debugging, and stuff, is it better to move ppm reading to Raspberry?
To answer one of your questions:
So what would happen if I'll use same 16bit timer for both pwm and ppm
reading? Would it work?
Yes. When your pin change interrupt fires you may just read the current TCNT value to find out how long it has been since the last one. This will not in any way interfere with the timer's hardware PWM operation.
Would it decrease speed drastically?
No. PWM is done by dedicated hardware, software operations running at the same time will not affect its speed and neither will any ISRs you may have activated for the corresponding timer. Hence, you can let the timer generate the PWM as desired and still use it to a) read the current counter value from it and b) have an output compare and/or overflow ISR hooked to it to create a software-extended timer.
Edit in response to your comment:
Note that the actual value in the TCNT register is the current timer (tick) count at any moment, irrespective of whether PWM is active or not. Also, the Timer OVerflow interrupt (TOV) can be used in any mode. These two properties allow to make a software-extended timer for arbitrary other time measurement tasks via the following steps:
Install and activate a timer overflow interrupt for the timer/counter you want to use. In the ISR you basically just increment a (volatile!) global variable (timer1OvfCount for example), which effectively counts timer overflows and thus extends the actual timer range. The current absolute tick count can then be calculated as timer1OvfCount * topTimerValue + TCNTx.
When an event occurs, e.g a rising edge on one pin, in the handling routine (e.g. pin-change ISR) you read the current timer/couter (TCNT) value and timer1OvfCount and store these values in another global variable (e.g. startTimestamp), effectively starting your time measurement.
When the second event occurs, e.g. a falling edge on one pin, in the handling routine (e.g. pin-change ISR) you read the current timer/couter (TCNT) value and timer1OvfCount. Now you have the timestamp of the start of the signal in startTimestamp and the timestamp of the end of the signal in another variable. The difference between these two timestamps is exactly the duration of the pulse you're after.
Two points to consider though:
When using phase-correct PWM modes the timer will alternate between counting up and down successively. This makes finding the actual number of ticks passed since the last TOV interrupt a little more complicated.
There may be a race condition between one piece of code first reading TCNT and then reading timer1OvfCount, and the TOV ISR. This can be countered by disabling interrupts, then reading TCNT, then reading timer1OvfCount, and then checking the TOV interrupt flag; if the flag is set, there's a pending, un-handled overflow interrupt -> enable interrupts and repeat.
However, I'm pretty sure there are a couple of library functions around to maintain software-extended timer/counters that do all the timer-handling for you.
what is unit of 700 and 2000?I guess usec.You have not exaplained much in your question but i identified that you need pulses of 25msec duration in which 700 usec on time may be 0 degree and 2000 may be for 180 degree now pulse input of each servo may be attached with any GPIO of AVR.and this GPIOs provide PWM signal to Servo.so i guess you can even control this all motors with only one timer.With this kind of code:
suppose you have a timer that genrate inturrupt at every 50 usec.
now if you want 700 usec for motor1,800 usec for motor 2,900 usec for motor 3 & 1000 usec for motor 4 then just do this:
#define CYCLE_PERIOD 500 // for 25 msec = 50 usec * 500
unsigned short motor1=14; // 700usec = 50x14
unsigned short motor2=16; // 800usec
unsigned short motor3=18; // 900usec
unsigned short motor4=20; // 1000usec
unsigned char motor1_high_flag=1;
unsigned char motor2_high_flag=1;
unsigned char motor3_high_flag=1;
unsigned char motor4_high_flag=1;
PA.0 = 1; // IO for motor1
PA.1 = 1; // IO for motor2
PA.2 = 1; // IO for motor3
PA.3 = 1; // IO for motor4
void timer_inturrupt_at_50usec()
{
motor1--;motor2--;motor3--;motor4--;
if(!motor1)
{
if(motor1_high_flag)
{
motor1_high_flag = 0;
PA.0 = 0;
motor1 = CYCLE_PERIOD - motor1;
}
if(!motor1_high_flag)
{
motor1_high_flag = 1;
PA.0 = 1;
motor1 = 14; // this one is dummy;if you want to change duty time update this in main
}
}
if(!motor2)
{
if(motor2_high_flag)
{
motor2_high_flag = 0;
PA.1 = 0;
motor2 = CYCLE_PERIOD - motor2;
}
if(!motor2_high_flag)
{
motor2_high_flag = 1;
PA.1 = 1;
motor2 = 16;
}
}
if(!motor3)
{
if(motor3_high_flag)
{
motor3_high_flag = 0;
PA.2 = 0;
motor3 = CYCLE_PERIOD - motor3;
}
if(!motor3_high_flag)
{
motor3_high_flag = 1;
PA.2 = 1;
motor3 = 18;
}
}
if(!motor4)
{
if(motor4_high_flag)
{
motor4_high_flag = 0;
PA.3 = 0;
motor4 = CYCLE_PERIOD - motor4;
}
if(!motor4_high_flag)
{
motor4_high_flag = 1;
PA.3 = 1;
motor4 = 19;
}
}
}
& tell me what is ESC?

Is this a good way to lock a loop on 60 loops per second?

I have a game with Bullet Physics as the physics engine, the game is online multiplayer so I though to try the Source Engine approach to deal with physics sync over the net. So in the client I use GLFW so the fps limit is working there by default. (At least I think it's because GLFW). But in the server side there is no graphics libraries so I need to "lock" the loop which simulating the world and stepping the physics engine to 60 "ticks" per second.
Is this the right way to lock a loop to run 60 times a second? (A.K.A 60 "fps").
void World::Run()
{
m_IsRunning = true;
long limit = (1 / 60.0f) * 1000;
long previous = milliseconds_now();
while (m_IsRunning)
{
long start = milliseconds_now();
long deltaTime = start - previous;
previous = start;
std::cout << m_Objects[0]->GetObjectState().position[1] << std::endl;
m_DynamicsWorld->stepSimulation(1 / 60.0f, 10);
long end = milliseconds_now();
long dt = end - start;
if (dt < limit)
{
std::this_thread::sleep_for(std::chrono::milliseconds(limit - dt));
}
}
}
Is it ok to use std::thread for this task?
Is this way is efficient enough?
Will the physics simulation will be steped 60 times a second?
P.S
The milliseconds_now() looks like this:
long long milliseconds_now()
{
static LARGE_INTEGER s_frequency;
static BOOL s_use_qpc = QueryPerformanceFrequency(&s_frequency);
if (s_use_qpc) {
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
return (1000LL * now.QuadPart) / s_frequency.QuadPart;
}
else {
return GetTickCount();
}
}
Taken from: https://gamedev.stackexchange.com/questions/26759/best-way-to-get-elapsed-time-in-miliseconds-in-windows
If you want to limit the rendering to a maximum FPS of 60, it is very simple :
Each frame, just check if the game is running too fast, if so just wait, for example:
while ( timeLimitedLoop )
{
float framedelta = ( timeNow - timeLast )
timeLast = timeNow;
for each ( ObjectOrCalculation myObjectOrCalculation in allItemsToProcess )
{
myObjectOrCalculation->processThisIn60thOfSecond(framedelta);
}
render(); // if display needed
}
Please note that if vertical sync is enabled, rendering will already be limited to the frequency of your vertical refresh, perhaps 50 or 60 Hz).
If, however, you wish the logic locked at 60fps, that's different matter: you will have to segregate your display and logic code in such a way that the logic runs at a maximum of 60 fps, and modify the code so that you can have a fixed time-interval loop and a variable time-interval loop (as above). Good sources to look at are "fixed timestep" and "variable timestep" ( Link 1 Link 2 and the old trusty Google search).
Note on your code:
Because you are using a sleep for the whole duration of the 1/60th of a second - already elapsed time you can miss the correct timing easily, change the sleep to a loop running as follows:
instead of
if (dt < limit)
{
std::this_thread::sleep_for(std::chrono::milliseconds(limit - dt));
}
change to
while(dt < limit)
{
std::this_thread::sleep_for(std::chrono::milliseconds(limit - (dt/10.0)));
// or 100.0 or whatever fine-grained step you desire
}
Hope this helps, however let me know if you need more info:)