Saturday 25 May 2019

Minimal ATMega328P - common error

Minimal ATMega328P - common error  - won't run at 3V3.

I've started using ATMega328P as a minimal version  of the Arduino Pro Mini operating a 3Vdc.
I have been breadboarding circuits using the various articles published on the Internet as a guide for the minimal wiring required for running an ATMega328P as an Arduino.

The problem I suffered was that the circuits would happily run at 5Vdc, but stop running as the voltage was reduced to 3.3Vdc.

There seems to be a lot of discussion about the problem on forums; with almost none of it relevant to me.

Eventually I realised the silly mistake that I was making, by taking minimal circuits from Internet without checking the details for myself.

Many articles for minimal ATMega circuits leave AVCC open, rather than tying it to VCC.
If AVCC is left open, the ATMega will work at 5Vdc but will stop working at around 3.5Vdc.
Once connected to VCC, the ATMega will work below 3Vdc.

In my case, it worked down to about 2.7Vdc, which is one of the settings for the Brown Out Detector, so I suspect, that is what has come into play.





Friday 24 May 2019

Slave I2C Interface for Serial GPS using Arduino Pro Mini 328P

Slave I2C Interface for Serial GPS using Arduino Pro Mini 328P


This post contains the code for creating a slave I2C device using an Arduino for a GPS receiver.
Hardware serial ports are precious on ATMega devices and I don't have enough available in my project to use on a GPS receiver, despite using an ATMega2560.
Software serial ports are often ok, but can suffer compatibility issues with other libraries in larger projects, and this has been prohibitive for me.
You can buy I2C GPS devices, and I do prefer to use them, but the one I was using failed, and I needed a quick solution to get my project back on the water without having to wait the 2 or 3 weeks for a replacement I2C GPS to arrive.
So, after an evening's research I was able to create a working I2C slave device using an Arduino Pro Mini 328P and integrate it with a regular serial GPS receiver.

The Arduino Pro Mini 328P running is at 16MHz and 3V3. Yes, I know its outside of specification with that combination of voltage and clock speed.

Overview

This simple sketch uses the very excellent TinyGPS++ to parse the GPS serial data and load the GPS object. Then we copy individual field values into our GPS structure which is mapped to linear Data buffer using UNION statement.
The Data buffer is then written out to the I2C interface in response to an I2C request.

Wiring

The Tx Pin from the GPS is connected to the RX pin of the Arduino Pro Mini 328P using a 1k resistor. This provides isolation to allow the Arduino to be programmed over the serial interface while the GPS is connected. If this were a direct connection, serial programming of the Arduino would not be possible without disconnecting the GPS.

Slave I2C GPS sketch


/*
    Name:       I2C_GPS.ino
    Created: 2 May 2019
    Author:     John Semmens
 Slave I2C Interface for Serial GPS using Arduino Pro Mini 328P
 ----------------------------------------------------------------
 Interface for converting a serial GPS to allow it to be used on an I2C bus.
 This uses the Arduino Pro Mini 328P running at 16MHz and 3V3. (Yes, i know its outside of specification!!!).
 This simple sketch uses the very excellent TinyGPS++ to parse the GPS serial data and load the GPS object.
 Then we copy individual field values into our GPS structure which is mapped to linear Data buffer using UNION statement.
 The Data buffer is then written out to the I2C interface in response to an I2C request.
 Wiring:
 The Tx Pin from the GPS is connected to the RX pin of the Arduino Pro Mini 328P using a 1k resistor.
 This provides isolation to allow the Arduino to be programmed over the serial interface while the GPS is connected.
 If this were a direct connection, serial programming of the Arduino would not be possible without disconnecting the GPS.
*/


#include <SPI.h>
#include <Wire.h>
#include "TinyGPS++.h"


// The TinyGPS++ object
TinyGPSPlus gps;


static const uint32_t GPSBaud = 9600;

#define  SLAVE_ADDRESS           0x29  //slave I2C address: 0x01 to 0x7F
#define  REG_MAP_SIZE            18 


// GPS variables
typedef union {
 struct {
  long Lat, Long;     // 2 x 4 bytes
  uint8_t Year, Month, Day;  // 3 x 1 bytes
  uint8_t Hour, Minute, Second; // 3 x 1 bytes
  int COG, SOG;     // 2 x 2 bytes SOG is in m/s
 };
 uint8_t Data[REG_MAP_SIZE];   //  = 18 bytes
} buffer_t;
buffer_t gps_data,buf;

boolean newDataAvailable = false;

void setup()
{
 Wire.begin(SLAVE_ADDRESS);
 Wire.onRequest(requestEvent);
 Serial.begin(GPSBaud);
}
void loop()
{
 while (Serial.available() > 0)
 {
  if (gps.encode(Serial.read()))
  {
   LoadRegisters();
   newDataAvailable = true;
  }
 }
}


void requestEvent()
{
 if (newDataAvailable)
 {
  for (int c = 0; c < (REG_MAP_SIZE); c++)
  {
   buf.Data[c] = gps_data.Data[c];
  }
 }
 newDataAvailable = false;
 Wire.write(buf.Data, REG_MAP_SIZE);
}


void LoadRegisters()
{
 gps_data.Lat = gps.location.lat() * 10000000UL;
 gps_data.Long = gps.location.lng() * 10000000UL;
 gps_data.COG = gps.course.deg() * 100;
 gps_data.SOG = gps.speed.mps() * 100; // m/s
 gps_data.Year = gps.date.year()-2000;
 gps_data.Month = gps.date.month();
 gps_data.Day = gps.date.day();
 gps_data.Hour = gps.time.hour();
 gps_data.Minute = gps.time.minute();
 gps_data.Second = gps.time.second();
 // Copy gps buffer to buf buffer
 for (int i = 0; i<REG_MAP_SIZE; i++)
  buf.Data[i] = gps_data.Data[i];
}


Master I2C Code Excerpt



void GPS_Read() {
 // V1.1 27/4/2019 Temp GPS reader for Temp I2C Arduino interface to a serial GPS



 Wire.requestFrom(0x29, 18);       // Ask for 18 bytes


 long Lat=0, Long=0;     // 8 bytes
 uint8_t Year =0, Month=0, Day=0;    // 3 bytes
 uint8_t Hour=0, Minute=0, Second=0; // 3 bytes
 int COG=0, SOG=0;     // 8 bytes


 for (int i = 0; i<4; i++)
  Lat = Lat | (long)Wire.read() << (i * 8);


 for (int i = 0; i<4; i++)
  Long = Long | (long)Wire.read() << (i * 8);


 Year = Wire.read();
 Month = Wire.read();
 Day = Wire.read();
 Hour = Wire.read();
 Minute = Wire.read();
 Second = Wire.read();


 for (int i = 0; i<2; i++)
  COG = COG | Wire.read() << (i * 8);


 for (int i = 0; i<2; i++)
  SOG = SOG | Wire.read() << (i * 8);


 NavData.Currentloc.lat = Lat;
 NavData.Currentloc.lng = Long; 


 // get the date and time from GPS into CurrentTime object.
 CurrentUTCTime.year = Year;
 CurrentUTCTime.month = Month;
 CurrentUTCTime.dayOfMonth = Day;
 CurrentUTCTime.hour = Hour;
 CurrentUTCTime.minute = Minute;
 CurrentUTCTime.second = Second;


 // get course and speed directly from GPS
 NavData.COG = ((float)COG/100);
 NavData.SOG_mps = ((float)SOG / 100);

};