SlashDevin / NeoHWSerial

Arduino HardwareSerial with attachInterrupt for RX chars
47 stars 21 forks source link

ATMega64A not working #5

Closed gpsodo closed 7 years ago

gpsodo commented 7 years ago

Hi,

I'm experiencing a very strange issue.

Using the MegaCore porting:

https://github.com/MCUdude/MegaCore

On the same PCB I can swap between ATMega2561 and ATMega64A, compiling properly the same code based on NeoGPS, with Serial as DEBUG_PORT and the GPS module connected to Serial1, the ATMega2561 works perfectly, while the ATMega64A simply can't read properly the GPS stream.

I even tried to adapt the very simple example of dual serial read and write from Arduino, to use the NeoHWSerial library unsuccessfully. At the same speed rate: 9600bps the two MCUs, that should be different only for the quantity of flash and EEPROM space, are giving totally different results. Every other function is working properly, so there is no doubt on the code and the settings used to build.

Is it possible the problem is related to the buffer?

Thanks

SlashDevin commented 7 years ago

I'm experiencing a very strange issue... Is it possible the problem is related to the buffer?

Good question. I'll have to review the differences between the standard Arduino MCUs and the 2561 and 64A. I'll also look for patches to the core HardwareSerial. It might be time to re-sync NeoHWSerial with HardwareSerial. I've been thinking of adding it to the Arduino Library Manager.

I even tried to adapt the very simple example of dual serial read and write

Could you post this, please?

gpsodo commented 7 years ago

First of all thanks for the super fast feedback.

The code is super simple:

#include <NeoHWSerial.h>

void setup() {
  // initialize both serial ports:
  NeoSerial.begin(9600);
  NeoSerial1.begin(9600);
}

void loop() {
  // read from port 1, send to port 0:
  if (NeoSerial1.available()) {
    int inByte = NeoSerial1.read();
    NeoSerial.write(inByte);
  }

  // read from port 0, send to port 1:
  if (NeoSerial.available()) {
    int inByte = NeoSerial.read();
    NeoSerial1.write(inByte);
  }
}

Nothing else than the standard dual serial example from Arduino core.

As I wrote before, I use the MegaCore as it extends the support to MCUs that are more compatible with my needs. In fact for a GPS device all I need are very few IOs, UART and more memory than a standard 328P offers. In fact the problem is not the NeoGPS library that's fantastic, but the use of ubg2 lib that takes a lot of memory to draw a 128x64 page on a graphic LCD, plus a lot of flash to incorporate fonts.

ATMEGA64 is perfect and the bigger brother 2561 has the advantage, compared to the 2560 of a MEGA, of being packed in a TQFP64, instead of 100, and also being cheaper, something important as the product must be as affordable as possible.

What really drives me crazy is the fact that this simple code, built for the 2561 works perfectly, for example it reads this from the GPS:

$GPTXT,01,01,02,MA=CASIC*27
$GPTXT,01,01,02,HW=ATGM332D,0060612200519*16
$GPTXT,01,01,02,IC=AT6558-5N-51-0C500000,JZ6A808-D3-022157*4A
$GPTXT,01,01,02,SW=URANUS4,V4.3.0.5*18
$GPTXT,01,01,02,TB=2016-07-18,16:50:32*42
$GPTXT,01,01,02,MO=GR*67
$GPTXT,01,01,02,BS=SOC_BootLoader,V6.2.0.2*34
$GPTXT,01,01,02,CI=01B94154*04
$GNGGA,,,,,,0,00,25.5,,,,,,*64
$GNGLL,,,,,,V,N*7A
$GPGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5*02
$GLGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5*1E
$GPGSV,1,1,00*79
$GLGSV,1,1,00*65
$GNRMC,,V,,,,,,,,,,N*4D
$GNVTG,,,,,,,,,N*2E
$GNZDA,,,,,,*56
$GPTXT,01,01,01,ANTENNA OPEN*25

while the same code, compiled and loaded on the ATMEGA64 outputs:

$EaT   Š,Šb0&©µbl�aj ×Ý.]I±o¹c«©5j
ÒATE±0L Š,’bHU(Õ:Ý0á 0‚7‚0*ÓSH$EaT Š,Š,’,ªª�CJU�1  ‚ ©Ê8“J ©—’7’‚1&Š7'ÓÒª2¥Sj
ÒATE±0L Š,0&    %=TUI L“    ‚*LTH$EaT  Š,Š,’,PŠ5UPQ*U

 *‚½SšI*&j
ÒATEÅ‚1 Š,’,PŠ5QAU5õDÊQKê]©3¦H$GEaT Š,Š,’,Šj�F”F”µF”F”-”F”F-””F”µF”F”*¦TH$%5
,ÉÅb,,‰b,‰r©5¦H$eQG‰b,‰b,É©3j
ÒAGT±,‰b,b0b9N)Ê,‰bb,%j
ÒAGSP)±1‰b,‰bb,‰b,Nr9N)Ê.Nb9N)ÊR3j
ÒAGŠ±,‰b,É©64Cá$EaT   Š,Š,’,PŠ5QAU5õI*Q*¦SH$%5C‰²±,‰‰b,‰bN¥Ój
ÒAVÕ±,‰b,‰bN¥‚j
ÒAGT±,,‰b0  ‚,Nr9N‰b,‰R4j
ÒuMA)±1‰bb,‰b,‰b,Nr9N)Ê.NN)Ê.NRš0C!ÒAGŠ±,,‰bVÉ©6SH

All I can think is about the buffer size, but I didn't get any idea about how to check where can be the difference. In fact the two guys should be pretty much the same core, with only flash and eeprom size as difference.

gpsodo commented 7 years ago

I actually have some more clue... the problem is in the setting of the baud rate in UBRR apparently.

I've just realized I missed a kind of very important information, probably. I'm running the two MCUs at 8MHz with Internal Clock source. While studying the code, I've realized that the configuration of the UBRR registry may be influenced by this so I've forced the use of single speed asynchronous mode:

void NeoHWSerial::begin(unsigned long baud, byte config)
{
  // Try u2x mode first
  // uint16_t baud_setting = (F_CPU / 4 / baud - 1) / 2;
  //*_ucsra = 1 << U2X0;

  // I force non-u2x mode
  uint16_t baud_setting = (F_CPU / 8 / baud - 1) / 2;

Forcing the operations at single speed I get an initial better result:

This line printed at setup to make sure the debug port is working properly
$GPTXT,01,01    ’bªµblox ag - www.]K,ë¹com*50
$GE…EÅ‚Šb‚Šb‚’,Hª aµG70xx ‚‚‚º‚‚‚‚ *ÓSHˆ:AQaQ±01,01,02,ROS=I�1.00BªÊ¢’JJu’º’‚Š2 L“¢šÒª’R5NC!ÒE…EÅ‚Šb‚Šb‚’bPªŠeUI�14.00¥*5
$GPTXT  Šb‚Šb‚2,PŠ5UAIYõAC SD PDo*šI©20
$E…Q±01,Šb‚’b
9QMQTUS=DONÕÊê꥚3
!ÒE…EÅ‚Šb‚Šb‚’,Lj�FFFFF””TÉD-FFFFF””TÉFF-FFF””””I*5
$GPRMC,ÉÅbbbb,,‰‰ÉJ©š
!ÒeEu±,,,,,,,,N¥‚j
$GPÔT±,,,,  b‚‚bÊ9)Êbbbb,I‰Âj
$GuM±A,1‰‰‰‰‰‰‰‰bbbbÊÊ.NN)ÊrÊ9)ÊrÊÊRš0
!Òu11±,,,,,V,JÉ¢j
$GPTXT  Šb‚Š,0&)9QMQQUS=INIT%ªj
$%5
±,‰‰‰‰‰‰‰bbr©53
!ÒeEu±,,,,,,,,Ji‚j
$Gu±,,,‰  b‚‚b9N)Êbbb,‰I‰Âj
$uM±A)bbb,‰‰‰‰‰‰bbÊÊr9N)ÊrÊ9)ÊrÊÊ*&j
$Gu11±,,‰‰ÉÅr©64
$GPTX  Šb0L    ’bANTSTAUÕÕz-*3B
á$GPRM(‰ÉÅbbbbb,‰‰ÉJ©šj
$GeQ±,,,,,‰‰ÉJi‚j
$Gu±,‰‰‰  b‚‚bÊ9.99,,‰‰‰I‰Âj
$GuM±A,1,,‰‰‰‰‰bbbb,NN)Ê,99.99)Êr9N¥&j
$GPGLŠ‰‰‰bb²±N%SHø

Some character is received correctly, while going ahead with the time everything get messed again.

gpsodo commented 7 years ago

Getting closer even more but still not a solution... at least not a standard solution for everyone... I solved the problem for my application but I think it lead to a problem of the specific MCU.

According to the datasheets from ATMEL the two MCUs have the same error parameters for the frequency of 8MHz and speed 9600bps:

For the ATMEGA2561 page 264 of the datasheet: For the ATMEGA64 page 225 of the datasheet:

U2X = 0 UBRR = 51 / U2X = 1 UBRR = 103 with Error Rating at 0.2%

Taking the formula from the code (NeoHWSerial.cpp line 77):

uint16_t baud_setting = (F_CPU / 8 / baud - 1) / 2;

we get (8000000 / 8 / 9600 - 1) / 2 = 51.5833

The number is about 1.15% higher than the one reported on the datasheet.

Also from the datasheet:

UBRR values which yield an actual baud rate differing less than 0.5% from the target baud rate, are bold in the table. Higher error ratings are acceptable, but the Receiver will have less noise resistance when the error ratings are high, especially for large serial frames

What I did is to force the baud_setting to the exact value reported in the datasheet:

  uint16_t baud_setting = 51;
  *_ucsra = 0 << U2X0;

The result is a better handling of the errors but is still not really OK.

So I went fine tuning, forcing the setting to different values, down to 50, 49 and 48 and I had good flow of data without errors, while mess starts again when going down to 47 for the value.

All I can think is that ATMEGA64 is less willing to handle errors, compared to other ATMEGA, and only on the Serial1 moreover. This may be due to the fact that ATMEGA64 was designed to be a replace for the ATMEGA103 and it can run in compatible mode, where the Serial1 is actually not active. I guess in the design some capability to handle errors gone wild.

In my case I can solve the problem with this manual tuning, as I don't need the code to be portable. Not sure if it makes sense to spend more time in analyzing the issue and/or place a note in the manual to alert users that some MCU may behave not as expected.

Anyway thanks for the great code you share with us!

SlashDevin commented 7 years ago

Glad I could help! :-)

The example data looks like the typical baud/clock rate problems. It made me wonder about the crystal frequencies and the fuse bits.

we get (8000000 / 8 / 9600 - 1) / 2 = 51.5833
The number is about 1.15% higher than the one reported on the datasheet.

Actually, the integer arithmetic goes like this:

(8000000 / 8 / baud -1) / 2 = (1000000/9599)/2
                            = (104/2)
                            = 52

So that's why 51 worked better.

I couldn't quite tell, but I wondered if you had tried this with HardwareSerial. Unless a version newer than 1.6.5r2 has new conditional code for the 64, I suspect it would act the same.

gpsodo commented 7 years ago

Yes the core HardwareSerial acts exactly the same. The ATMEGA64 is not officially supported by Arduino so I think nobody had this issue before.

Anyway I believe is an hardware problem due to the way the IC is designed for being compatible with the old ATMEGA103 now at EOL.

SlashDevin commented 7 years ago

Yes the core HardwareSerial acts exactly the same.

Great, thanks for the follow up so I can close this.