Log data from MPU6050 through serial (UART) fails (data loss) - c

here is the problem I am facing. I have interfaced my ATmega328P with a 6-axis IMU (MPU6050 with the GY521 breakout board). I can read data through the TWI interface (Atmel's I2C) and send it to my PC (running Ubuntu) via the UART. I am using custom-built libraries for both these communication protocols, but they are pretty standard and seem to work just fine. The goal of the project is to compute orientation data from the IMU readings in real-time, say at 100 Hz.
The main problem is that I cannot log data from the device at 100 Hz (not even at 50 Hz). The orientation filter I am using (here) requires a quite high frequency and 100 Hz turned out to work fine (tested offline acquiring data from another device).
Right now, I am using the 16-bit timer of the ATmega328P to sample data at 100 Hz and this seem to work, as I have added to the ISR a line to toggle the built-in LED and it looks to me that it is blinking at 100 Hz (I can barely see it turning on and off). In the same ISR, I read the values from the inertial sensor and, just to log them, send these values through the serial port. Every 10 ms (maximum), I send 9 floats (36 bytes) with a baud rate of 115200. If I use the Arduino IDE's Serial Monitor to visualize this data stream, I notice something very weird, as in the following screenshot.
https://imgur.com/zTBdkhv
As you notice taking a look at the timestamps, there is a common 33 ms delay every 2 or 3 sets of samples received. Moreover, I get roughly the 60% of the data. For example, an acquisition of 10 seconds only gets me less than 600 samples (per each variable) instead of 1000. Moreover, I tested the same sending only one variable through the UART (i.e. only a single float, 4 bytes) and this results in the same behavior!
By the way, I am exploiting the following to send each byte (char) via the UART interface.
void writeCharUART(char c) {
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;
}
Even though my ISR runs at 100 Hz (LED blinking seem to confirm that), data loss may occur at the level of the TWI transmission. To prove that, I modified the code of the ISR to send just a normal char (T) instead of data from the MPU and I got a similar behavior. Something like this:
00:10:05.203 -> T
00:10:05.203 -> T
00:10:05.236 -> T
00:10:05.236 -> T
00:10:05.236 -> T
00:10:05.236 -> T
00:10:05.269 -> T
So, I guess there is something wrong with the UART library and I actually sample at 100 Hz, but the logging frequency is much lower (and not constant). How can I solve this issue and/or debug the UART library? Do you see other reasons to justify this issue?
EDIT 1
As pointed out in the comments, it seems to be a problem of the receiving software that limits the frequency to ~30 Hz by some sort of buffering. To confirm that, I programmed the ATmega328P with the following code (this time using the IDE).
void loop() {
Serial.println("T");
}
At first, I thought there was no delay this time, but I could find it after 208 samples. So, there are ~200 samples received at the same timestamp and another bunch of samples after 33 ms. This may be proof that the receiving software introduces this delay.
I also tested a simple serial monitor that I had developed in C and, even though there is no timestamp functionality, I am also loosing samples if I fix the duration of the acquisition sampling at 100 Hz. My serial monitor is based on the termios.h library, but I could not find any documentation about its way of buffering incoming data.

There are two issues here:
You are missing messages. You checked the sample rate just with your eyes and told us that you can still see a very fast blinking. Depending on the colour of your LED, the ambient light, your physical state, and your eyes this could mean anything from 30 Hz to 100 Hz.
I would not trust my eyes to estimate and rather use an oscilloscope or a frequency counter to measure.
You could reduce the frequency of the LED blinking to 1Hz or even lower by dividing in software. Such a low frequency can be measured by hand via a stop watch. For example count 30 blinks and check the time needed for this.
Add a counter to the message and increment it with each message. You will see it right away if you're losing data.
The timestamps seem to indicate that the messages are "clustered" at about 30 Hz.
I'm guessing that the source of the timestamp in running at 30 Hz. So it can not give you more accurate values.

I kind of solved my issues! First of all, thanks to the comments I have checked that my ISR was correctly running at 100 Hz. Doing so, I could be sure that the problem where somewhere else, namely in the UART communication.
I found this very helpful: Linux, serial port, non-buffering mode
Apparently, the Serial Monitor provided by the Arduino IDE uses exploits the termios.h library and uses its default settings. I checked also the user manual and switched to the polling-read mode. Quoting from the user manual
If data is available, read(2) returns immediately, with the lesser of the number of bytes available, or the number of bytes requested. If no data is available, read(2) returns 0.
Hence, I switched back to my serial monitor code and changed the initPort() function adding the following lines of code.
struct termios options;
(...)
options.c_cc[VTIME] = 0;
options.c_cc[VMIN] = 0;
I noticed right away a much higher data frequency in the terminal. I kept the 1 Hz LED blinking in the ISR and there is no period stretching. Moreover, an acquisition of 10 seconds this time gave me roughly 1000 samples per variable, consistent with a sampling rate of 100 Hz.
On the AVR side, I also changed the way I send data through the UART. Before, I was sending 9 floats like this:
sprintf(buffer, "%f, %f, %f", value1_x, value1_y, value1_z);
serial_print(buffer); // no "\n" sent here
sprintf(buffer, "%f, %f, %f", value2_x, value2_y, value2_z);
serial_print(buffer); // again, no "\n" sent
sprintf(buffer, "%f, %f, %f", roll, pitch, yaw);
serial_println(buffer); // "\n" is sent here once the last data byte is sent
Now, I replaced all this with a single call to the function serial_println() and I write only 6 floats to the buffer.

Related

How to handle asynchronous input and synchronous output?

I am currently working on a project where I have USART input and SAI(Serial Audio Interface, similar to SPI) output on an STM32 system.
I created a circular buffer which act as pinpong buffer(double buffer) structure. The input samples which received from USART are stored in this buffer where the head pointer points. When SAI peripheral requests new data, data is pulled from this buffer's tail pointer.
At the start of my code I wait until half the buffer is filled then activate SAI. SAI outputs at constant rate which is 40kHz. Input samples are received from external device's USART at approximately at same rate 40kHz.
Ideally, I expect the difference between my head and tail pointer to be constant.
I also implemented a protection mechanism which makes the Tail pointer wait and output the last sample from SAI until half of the buffer to fill when two pointers are pointing at same location.
The code works at start. The problem is when some time passes like approximately 2 minutes we see the head and tail pointers are pointing at the same location which creates discontinuity in our samples. Which means the one pointer is slow or fast than expected. I am sure of SAI protocol outputting 40kHz constantly (I checked it with scope). However, I am not so sure about accuracy of USARTs timing. I cannot modify the external USART device's code and I cannot change the output rate 40kHz it must be this value.
Is there a another way (maybe other than ping pong buffer method) to handle asynchronous input and synchronous outputs?
If what you are saying is you are receiving (continuous) serial data from some external device, and then you are forwarding it out some interface of your own at some rate...based on your clock. Even if the data is the same format and the clocks are "the same", then a buffer overflow is expected somewhere.
Same thing happens with ethernet or any other source if 1) continuous at line rate 2) source for the input and source for the output are a different clock 3) there are guaranteed to be differences in the reference clocks, if the input source clock is a little faster then so long as the stream stays continuous and at line rate, then you will overflow eventually.
The clocks change with temperature and voltage so the delta can change.
Possible to even reduce the percentage of the data you output from the input and still overflow if the input is continuous. Depends on if your output is also at line rate or of you have margin and the margin can overcome the difference in the clocks.
Also remember uarts hardly run at that exact rate, the use clock dividers and get close, you can have two computers using uarts at the same rate and the delta can be relatively large and the overflow can happen very soon. For uart to work the clock has to only be good enough to get through one character so can be several percent off if not more than that, even if the oscillator is very good and no plls and both sides use the same reference clock (but not the same uart, clocking system, etc).
If you increase your output rate or reduce the data being output so that it is not at line rate then the problem may go away or may take hours or days before it happens...
if I have misunderstood the problem, forgive me, I will delete this answer.

STM32F4 SPI idle time between each data frame, why is it too long?

I am trying to read 256 bits in less than 32 ms from SPI, the data frame is 16 bits. My problem here is SPI driver has a long idle time between each 16 bits. See this picture, as you can see I am reading 64 bits (highlighted by the red rectangle), and there are long pauses between each frame. I don't find anything in the SPI specification about it.
I am testing on an STM32F407 board from Keil and SPI is initialized by the Keil CMSIS default driver.
Is there any way to reduce this idle time?
You can show the code snippet for rest of people to be able to help, but anyways ı remember coming across the same problem.Here is how i reduce that extra time gap;
Use Direct Register Access instead of Functions, both for sending and flag checking.
If that also wont work, instead of flag checking you can use simple for loop for some amount of delay to keep to clock signal low enough but not much, check from ossilloscope to find the accurate iteration number.
Also make sure that no interrupt is being served while you send your message,there might be an ISR interrupting between the end of transmission and the code that pulls the CLK signal high. If you have ISRs that occur asynchronusly and big in size this is probably the case.
SPI1->DR = (uint16_t)Data;
while(!(SPI1->SR & SPI_SR_TXE));
PS: I ve done this on Cortex-M3,no RTOS,STD Peripheral Library.
Edit: Also try with busy Flag only,thus checking EOC only while(SPI1->SR & SPI_SR_BSY);

Do I need buffering in this case, if so, how?

I have an issue where the rate the data is sent to serial port (50Hz) is way faster than the serial port can receive (8Hz).
Basically, what I have is, data is sent from fpga vhdl block to NIos II system, Nios II system sends the data to serial port, Matlab access the serial port to real time plot the graph.
I am using Quartus 12.1 sp1, vhdl and Nios II programmed in C code for DE0-Nano. In Qsys, I am using a few cores such as timer, sdram, uart, pios etc.
I dont know if this is feasible. What I am tempted to do, since the rate I can plot a graph is slower than the rate the incoming signal is read.
If I know the beginning of every incoming signal let say this point is indicated by a count=0, everytime I detect count=0, I know it is the beginning of each incoming signal, I keep track of how many cycles, let say, I only want to plot the graph every 10 cycles of the incoming signals, I plot cycle no.1, skip 10 cycles, plot cycle no. 11, skip cycle no.12 to 20, next plot cycle no. 21 etc ...in other words, it means I will lose some cycles in between which I am okay with this.
Do you think this method make any sense?
Do I still need buffering in this case?
How do I know how many cycles I need to skip? I assume it would depend on the incoming signal rate (50 Hz) and plot/receive rate (say 8 Hz), But I am not entirely clear about how to calculate...
The incoming signal (50 Hz) is from fpga vhdl, NIos system can read at 50 Mhz, but serial port receive rate or Matlab plot rate is 8 Hz.
Appreciate any input...thank you

Increase Beaglebone Black ADC sampling rate?

I'm working on a project that requires the use of a microcontroller, and for this reason, I decided to use the Beaglebone Black. I'm still new to the Beaglebone world and I'm facing some problems that I hope you guys can help me with.
In my project I will have to continuously read from all the 7 analog read pins and do some processing accordingly. My question is, what will be the fastest programming language to do so (I must read as much samples as possible and in a very short time!) and how to increase the sampling rate from KHz to MHz?
I tried the following codes:
Javascript Code:
var b = require('bonescript');//this variable is to refer to my beaglebone
time = new Date();
b.analogRead("P9_39");
console.log(new Date() - time);
this code will simply perform one analog read and will print out the time needed to perform the read. Surprisingly, the result was 111ms!! which means that my sampling rate is 10 if I'm not wrong.
An alternative was to use pyhton:
import Adafruit_BBIO.ADC as ADC
import time
ADC.setup()
millis = int(round(time.time() * 1000))
ADC.read_raw("P9_39")
millis = millis = int(round(time.time() * 1000)) - millis
print millis
this code took less time (4ms) but still, if I wanted to read form the 7 analog input pins, I will only be able to read around 35 samples from each.
Using the terminal:
echo cape-bone-iio > /sys/devices/bone_capemgr.*/slots
time cat /sys/devices/ocp.3/helper.15/AIN0
############OR############
time cat /sys/devices/ocp.3/44e0d000.tscadc/tiadc/iio\:device0/in_voltage0_raw
and this took 50ms.
I want my sampling rate to be something in MHz. How can I do so? I know that the Beaglebone Black is capable of that but I could not find a clear way to do so. Any help is appreciated.
Thanks in advance.
Sampling rate of AM335x ADC is 200K (link). This means you won't get into MHz range with stock BeagleBone Black ADC.
To get something working with a latency of 5 µs in non-real-time OS like Linux is impossible. You will be at a mercy of OS to schedule your execution thread. Other kernel threads will take priority and will preempt your thread, even if you assign it the highest scheduling priority.
From my experience with digital IO on BeagleBone Black, I stated seeing missed frames starting around 1K samples per second. Now, it will depend on your level of tolerance to missing samples -- if you only need working semi-reliably you can probably squeeze out 10 K samples per second by switching to C/C++ and increasing priority of your process with nice --10 ... command. However if you cannot tolerate missed frames, you have to do one of these:
Bypass OS entirely and write C program for naked AM335x processor (no OS).
Use another hardware -- an ADC with a buffer to accumulate samples while your program is preempted.
Use PRUSS processors on BBB. They run at 200 MHz, so if you have a tight loop with e.g. 20 assembly instructions you will get reliable sampling rate of 10 MHz. That is if you had a faster ADC in the first place, and of course it would handle the stock 200 KHz ADC easily.
I personally went with option #3 and was happy to see my device perform sub-millisecond GPIO operations extremely reliably.
Use 127 beaglebone blacks plugged into 127 usb hub ports and breakout visual basic and write a usb program to automatically sequencially fire 127 beagle bones 1 after the other and read the data in a textbox...You will get around 16 mhz / msps consective adcs per fast cpu with say windows 10....lyj2021
You may have over lapping data...But you can track this with each fire of each beagle bone black...consecutively...

GPS - Speed doesn't update as should - EM408 & Arduino Mega & GSM

I'm developing a system that will get the GPS signal and send it though the GSM with information about position, speed and temperature from some digital sensors.
Currently I'm using the GPS EM408, the Arduino mega plus the GSM board (official one).
The problem is that the GPS (by the library TinyGPSPlus) gives me the same speed for long time or sometimes give me 0km/h.
The sketch works like this:
loop()
{
getGPSData() - ~ 1 sec to execute and take one data from the GPS.
getSensors() - ~ 1 sec to execute and take one data from the digital sensors.
sendData() - ~ 6 n 10 secs to send the data through the internet.
}
The whole process takes around 10 ~ 15 secs to be completed.
If I remove the sendData() and the system starts getting the GPS information each second the speed value works perfectly but if I get the data from the GPS each 12 secs (because of the GSM delay) the speed doesn't work as expected.
I understand that the problem is because the library TinyGPSPlus calculate the speed between two points and the getGPSData() only takes one information each loop and the next point has 15 secs of difference.
Although I've added a "for(i=0;i<=4;i++)" to the getGPSData() enforcing it to get at least 4 times of position before the GSM send it over the internet, now is working better but still getting the wrong value or sometimes it freezes to the same speedy for long time.
I've tried to add a second board and put both to communicate with I2C turning it "dual core", where one board will be getting the data from the GPS each second and another one will send though the data each 15 secs, but the GSM freezes sometimes when the I2C is connected :(.
Does anyone has any clue how to do it?
It is not good to add "for(i=0;i<=4;i++)" loop like you tried and furthermore to add duplicate device - so far from the solution. Instead you should unbind getGPSdata() from sendData() in other words separate the calls of them into different tasks.
I can imagine it is a simple Round-robin scheduling provided with that library, not a complete RTOS in there, isn't it? Nevertheless, put them into different tasks, loops or whatever. Probably you will need to arrange a buffer to collect GPS data to and sending out this from the buffer from a different loop.
Hope it helps.

Resources