You need an application to track where your son goes with your car?
You came to the right place.
Today, I will just add a report I wrote last year about a GPS module I
had put on one of the earliest versions of Soroban. This morning, I set this
module on the latest version, compiled, and it just works.
The module
This is a GPS module with a patch antenna. It is quite small. I would like
it smaller, but it is a good compromise as it is not very expensive. A little
bit less than 27 mm square.
Now here is a view of its thickness, a little bit under 9mm. Sorry for the
quality of the photographs, I am not a specialist, and the camera I use
is one of the earliest 3 MPix I bought in 2000.
The platform
This time (surprise?) I used a new version of Soroban to which I have
added a nice lithium polymer battery. According to the GPS specs, it
should use at most 28 mA. The rest of the board (MSP430 + RTC +
LCD) consume together about 5mA at 8MHz, which brings us to 33. Make
it 40 worst case, and suppose the 650 mAh of the battery are an
optimistic label, then we can maybe count on 400 mAh which enables
to have have about 10 hours of continuous use, which should be OK for
most of your bicycle trips.
Wiring the module on Soroban
Before wiring, I had to punch a hole to leave some room for the 3
antenna pins that are off-pitch. They are not used anyway because
the module has a patch antenna.
Wiring the module is extremely easy. The module outputs data through
serial port (UART), so we need to wire Rx, Tx. As USART1 is already
used for LCD and RTC in SPI mode, we are going to use USART0, so
there will be no need to switch between UART and SPI mode.
So Rx and Tx are wired to pins 32 and 33.
Now, the specs of the module say that the reset level has to be around
2V. As the MPU is powered in 3.3V, we are clearly out of range. I have
tried to use 3.3V for reset, it didn't work (luckily, the module survived).
So I have set up a resistor bridge to divide the reset signal by 2.
As you can see on the next picture, I have used 2 chip resistor (51k).
The left one is wired to MSP430's port 4.3 (pin 39). The right one is
grounded, and the middle is used for the GPS module reset signal.
it is not exactly 2V, but I don't have proper resistors to get exactly
2V. Anyway it works. Here is a closeup of the chip resistors.
Software
The software part is quite easy.
I had to write functions to manage the UART. Sorry, this is quick & dirty
code and I would recommend to use it with extreme care, but it is good
enough for experimentation.
UART source code
Here is the .h file: (types.h defines uint8, uint16, etc...)
- Code: Select all
#ifndef _USART_H_
#define _USART_H_
#include <msp430x16x.h>
#include "types.h"
// Non-realtime functions. These are configuration functions.
// This is to be changed to reduce the number of functions.
// USART0 inits:
void InitUsart0(void);
void USART0Enable(void);
void USART0Disable(void);
void SetUSART0Rate(uint16 freq);
void Enable_IE_USART0(void);
void Disable_IE_USART0(void);
void SetTimeout(uint8 which, uint16 t);
// Realtime functions (USART0 send and receive functions)
void USART0SendByte(uint8 byte);
void USART0SendBuffer(uint8 * buf, uint16 len);
uint8 USART0RecvByte(void);
uint16 USART0RecvBuffer(uint8 * buf, uint16 len);
#endif
Now here is the C file. globals.h defines the external crystals frequencies
(XTFREQ is used here). Timeout has been set so that the function does
not hang. By setting the timeout properly, you can decide how you will
receive a block of data. Basically you receive 2~300 consecutive bytes,
then there is a break. In the consecutive bytes, the maximum delay
can be set to the transmission time for 3 bytes for example. If you have
to wait more, this is likely to indicate the end of the buffer, so you can
return.
- Code: Select all
#include <msp430x16x.h>
#include "USART.h"
#include "globals.h"
static uint16 timeout0;
//---------------------------------------------------------
// Initializatons USART0
//---------------------------------------------------------
void InitUsart0(void) {
// P3.4,5 = USART0 TXD/RXD
P3SEL |= 0x30;
P3DIR |= 0x10; //P3.4 = output direction
P3DIR &= ~0x20; // P3.5 = input direction
// 8-bit, UART
U0CTL = (CHAR | SWRST);
// UCLK= SMCLK
U0TCTL |= SSEL1;
// Set baud rate to 9600 (might be changed later).
SetUSART0Rate(9600);
// Initalize USART state machine
U0CTL &= ~SWRST;
// Enable USART0 RX interrupt
USART0Enable();
Enable_IE_USART0();
}
// Enable USART0 TXD/RXD
void USART0Enable(void) {
// Enable module
ME1 = UTXE0 | URXE0;
// Remove reset flag
U0CTL &= ~SWRST;
}
// Disable USART0
void USART0Disable(void) {
// Set reset flag
U0CTL |= SWRST;
// Disable module
ME1 &= ~UTXE0;
ME1 &= ~URXE0;
}
// Set USART baud rate
void SetUSART0Rate(uint16 BaudRate) {
uint16 divisor;
uint8 * div;
// Temporarily disable the module
U0CTL |= SWRST;
// Calculate the divisor
divisor = XTFREQ / BaudRate;
timeout0 = 2* divisor;
// Superpose a uint8 pointer
div = (uint8 *)(&divisor);
U0BR1 = div[1];
U0BR0 = div[0];
// Re-enable the module
U0CTL &= ~SWRST;
}
// Enable USART0 Rx Interrupt
void Enable_IE_USART0(void){
IE1 |= URXIE0;
}
// Disable USART0 Rx Interrupt
void Disable_IE_USART0(void){
IE1 &= ~URXIE0;
}
void SetTimeout(uint16 t) {
timeout0 = t;
}
//---------------------------------------------------------
// Transmit and receive USART0
//---------------------------------------------------------
// Send a single byte
void USART0SendByte(uint8 byte) {
IFG1 &= ~UTXIFG0;
/* Send the byte */
U0TXBUF = byte;
/* Wait for the byte to be sent */
while (!(IFG1 & UTXIFG0)) { }
}
void USART0SendBuffer(uint8 * buf, uint16 len) {
int i;
for(i = 0 ; i < len ; ++i) {
USART0SendByte(buf[i]);
}
}
// Receive a single byte
uint8 USART0RecvByte(void) {
uint8 retval;
IFG1 &= ~URXIFG0;
// Wait for the receive flag.
while (!(IFG1 & URXIFG0)) {}
retval = U0RXBUF;
return retval;
}
uint16 USART0RecvBuffer(uint8 * buffer, uint16 len) {
uint16 retlength = 0;
uint16 timeout;
while(retlength < len) {
timeout = 0;
IFG1 &= ~URXIFG0;
while(!(IFG1 & URXIFG0) && (timeout++ < timeout0)) {}
if(timeout >= timeout0) break;
buffer[retlength] = U0RXBUF;
retlength++;
}
return retlength;
}
That's it.
GPS module source code
The GPS source code is pretty straightforeward.
- Code: Select all
#ifndef _GPS_H_
#define _GPS_H_
#include "Types.h"
#define GPSRESET 0x08
// GPS specific
void InitGPS(void);
void ResetGPS(void);
uint16 ReadNMEA(void);
// Custom functions for parsing NMEA data
uint8 GetAltitude(uint8 * str);
uint8 GetCap(uint8 * str);
uint16 GetCompass();
uint8 GetGMTTime(uint8 * str);
uint8 GetLatitude(uint8 * str);
uint8 GetLongitude(uint8 * str);
uint8 GetLocalTime(uint8 * str);
uint8 GetNSats(void);
uint8 GetSpeed(uint8 * str);
void SetTimeZone(int8 zone);
#endif
As for the implementation, first I knew that I would get some NMEA
data. When looking at that home page, I understand that NMEA is nothing
more than plain text data. The module spec says that I would get
1 set of informations every second. After a few experiments, I found
out that the data is about 250 bytes coming in a burst every second.
Sometimes there is more, but the most I had was about 400 bytes.
So I decided to receive data in a 512 byte buffer.
So in the implementation, I have setup a static buffer to keep the
NMEA data. Then I also reserved a 18-character string (remember
that the display PW10164LCD has 8 lines of 17 characters). This string
will be used to extract NMEA data from the NMEA buffer.
Then, last thing is to have a static timezone in order to setup Japanese
time (in my case) or any other time zone.
Here is one part of the implementation (I skip data retrieving functions
which are plain string processing routines).
- Code: Select all
#include <msp430x16x.h>
#include "GPS.h"
#include "Usart.h"
#include "Clock.h"
#include "AltitudeFilter.h"
// This implementation is a "hardcoded" implementation which assumes
// that the GPS module has been set to UART0.
// This file keeps a buffer of the NMEA data.
#define NMEA_BUF_LEN 0x200
// Static variables and buffers
static uint8 NMEABuffer[NMEA_BUF_LEN];
static uint8 RawString[18];
static int8 timezone;
// Configures the UART first for 9600 baud. This will be subject to
// changes later.
void InitGPS(void) {
empty_buffer(NMEABuffer, NMEA_BUF_LEN);
SetTimeZone(9);
ResetGPS();
InitUsart0();
}
void ResetGPS(void) {
P4DIR |= GPSRESET;
P4OUT &= ~GPSRESET;
delay(100);
P4OUT |= GPSRESET;
}
uint16 ReadNMEA(void) {
uint16 len;
empty_buffer(NMEABuffer, NMEA_BUF_LEN);
len = USART0RecvBuffer(NMEABuffer, 0x200);
return len;
}
That's it. Once you get a buffer of NMEA data, you have all what you
need and you can extract or ignore any data, store it in the SD card
and so on.
Some results
Here are photographs of the results:
Note that I was walking slowly in order to take a picture. I was heading
south, and therefore the heading 179 degrees seems to be right. I have
developed a kind of compass with the indications N, NE, E, SE, S, SW,
W, NW. As you can see on the picture, the SE indication is on the left,
which sounds true if you are heading south.
The chip which is on the board is a pressure sensor. The GPS is not
accurate in altitude, so this will bring some more accuracy, but this
will be described in another article.
The second picture shouws the screen detail:
Conclusion
We have shown that new hardware can be added to Soroban with minimal
trouble. A complete GPS receiver with potential storage capability (let's
be honest, it was not developed here) could be developed by adding
7 wires only. As it is only a demonstration, the source code has still
a lot to be improved.
Another thing that could be improved would consist in constantly storing
the coordinates into the CPU flash. The startup of the module takes more
than 1 minutt because it is a "cold start" (the module has no hint about
its position at start up). So if we store the parameters and give hints
to the module at start-up, startup will be a lot faster.
NB
The whole working source code can be provided to customers buying
Soroban and a GPS module. The GPS module will be available now.
Careful:
Recording is not implemented in the current application, although all
the pieces exist. Note that we provide routines to access the SD card,
but these are raw routines, not a file system implementation.
