Reputation: 41
I am currently working on the integration of a "shunt" type sensor on an electronic board. My choice was on a Linear (LTC2947), unfortunately it only has an Arduino driver. I have to translate everything in C under Linux to be compatible with my microprocessor (APQ8009 ARM Cortex-A7). I have a small question about one of the functions:
int16_t LTC2947_wake_up() //Wake up LTC2947 from shutdown mode and measure the wakeup time
{
byte data[1];
unsigned long wakeupStart = millis(), wakeupTime;
LTC2947_WR_BYTE(LTC2947_REG_OPCTL, 0);
do
{
delay(1);
LTC2947_RD_BYTE(LTC2947_REG_OPCTL, data);
wakeupTime = millis() - wakeupStart;
if (data[0] == 0) //! check if we are in idle mode
{
return wakeupTime;
}
if (wakeupTime > 200)
{
//! failed to wake up due to timeout, return -1
return -1;
}
}
while (true);
}
After finding usleep() as equivalent for delay(), I can not find it for millis() in C. Can you help me translate this function please?
Upvotes: 4
Views: 8776
Reputation: 21
I hope this be useful. Works for me under Lubuntu 20.04 LTS.
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
struct timeval __millis_start;
void init_millis() {
gettimeofday(&__millis_start, NULL);
};
unsigned long int millis() {
long mtime, seconds, useconds;
struct timeval end;
gettimeofday(&end, NULL);
seconds = end.tv_sec - __millis_start.tv_sec;
useconds = end.tv_usec - __millis_start.tv_usec;
mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;
return mtime;
};
int main()
{
init_millis();
printf("Elapsed time: %ld milliseconds\n", millis());
return 0;
}
Note:
Based on the discussion in comments (with dear @MarcCompere), I must mention that the conversion of seconds
and useconds
to mtime
in the millis
function is rounded by adding 0.5
(read comments to understand how!); but the 0.5
can be removed. It depends on your application. If you are using millis for accurate time measurement then add it to lower the "Mean Squared Error (MSE)" of conversion statistically. But if you need timing for general logic-based decisions (or closer behavior to that of Arduino), then the floor (natural behaviour when casting in this case) can be considered as the better option, so do not add the 0.5
.
Upvotes: 1
Reputation: 39
#include <time.h>
unsigned int millis () {
struct timespec t ;
clock_gettime ( CLOCK_MONOTONIC_RAW , & t ) ; // change CLOCK_MONOTONIC_RAW to CLOCK_MONOTONIC on non linux computers
return t.tv_sec * 1000 + ( t.tv_nsec + 500000 ) / 1000000 ;
}
or
#include <sys/time.h>
unsigned int millis () {
struct timeval t ;
gettimeofday ( & t , NULL ) ;
return t.tv_sec * 1000 + ( t.tv_usec + 500 ) / 1000 ;
}
The gettimeofday() version probably does not work on non linux computers.
The clock_gettime() version probably does not work with old C compilers.
The arduino millis() returns unsigned long, 32 bit unsigned integer. Most computers are 32 bit or 64 bit, so there is no need to use long except on 16 bit computers like arduino, so these versions return unsigned int. If you want to measure a time period longer than 50 days in milliseconds, or if you want the number of milliseconds since the beginning of unix in 1970, you need a long long (64 bit) integer.
If a computer clock has the incorrect time, the operating system or system administrator or program which synchonizes the computer clock with internet clocks may change the computer clock to the correct time. This will affect these functions, especially the gettimeofday() version. Usually there is a big change in the computer clock when the computer boots, connects to the network, and synchonizes the computer clock with the network time server. But most programs are not running this early in the boot process, and thus are not affected. Usually other changes to the computer clock are very small, and the effect on other programs is very small. So usually changes to the computer clock are not a problem.
The clock_gettime() requires a clock id.
CLOCK_MONOTONIC is not affected by discontinuous jumps in the system time, but is affected by incremental adjustments, and does not count time computer is suspended.
CLOCK_MONOTONIC_RAW is linux only, not affected by discontinuous jumps in the system time, not affected by incremental adjustments, does not count time computer is suspended.
CLOCK_BOOTTIME is linux only, not affected by discontinuous jumps in the system time, but is affected by incremental adjustments, does count time computer is suspended. It counts the time since the computer booted.
CLOCK_REALTIME is affected by discontinuous jumps in the system time, and by incremental adjustments. It does count the time the computer is suspended. It counts standard unix time (time since the beginning of unix in 1970).
I think CLOCK_MONOTONIC_RAW is the best choice for linux, and CLOCK_MONOTONIC is the best choice for non linux. Usually millisecond time is used to measure short periods of time, like how long it takes for part of a computer program to run. In a short period of time, there will probably be no changes to the computer clock, and the computer will probably not be suspended, so any clock id will work, so the choice of clock id is not important.
Precise time measurements are unreliable on multitasking computers because the time measurement might be interrupted. Errors are usually small. Sometimes this is a problem, and sometimes it isn't. If you need more precise time measurements, you need dedicated hardware which cannot be interrupted. Some computers have such hardware built in. For example, if a program uses software pwm, changes to the output will be delayed if the computer is interrupted at the time the computer needs to change the output. But if the program uses hardware pwm, the hardware pwm controller cannot be interrupted, and will change the output at the correct time.
Tested on a raspberry pi.
Upvotes: 2
Reputation: 7409
Arduino millis()
is based on a timer that trips an overflow interrupt at very close to 1 KHz, or 1 millisecond. To achieve the same thing, I suggest you setup a timer on the ARM platform and update a volatile unsigned long
variable with a counter. That will be the equivalent of millis().
Here is what millis() is doing behind the scenes:
SIGNAL(TIMER0_OVF_vect)
{
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;
m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}
timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;
}
unsigned long millis()
{
unsigned long m;
uint8_t oldSREG = SREG;
// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
cli();
m = timer0_millis;
SREG = oldSREG;
return m;
}
Coming from the embedded world, arguably the first thing you should do when starting a project on a new platform is establish clocks and get a timer interrupt going at a prescribed rate. That is the "Hello World" of embedded systems. ;) If you choose to do this at 1 KHz, you're most of the way there.
Upvotes: 2