0
\$\begingroup\$

I am currently working on a GPS module from Quectel (L70). I try to interface it with MCU TM4C via UART1 to query some position then store it in the memory of the MCU and then to push those information to the UART2 connected to computer so I can see them in Putty or TeraTerm. I am working using CCS6 from TI.

I have some difficulties to implement this. The specs of the GPS module are here.

In my understanding, I am supposed to initialize UART1 port of MCU, then send a printf or put command to UART including PMTK or NMEA command such as printf("$PMTK010,001*2E<CR><LF>") , then the module should reply on the other wire with something like $GPRMC,013732.000,A,3150.7238,N,11711.7278,E,0.00,0.00,220413,,,A*68<CR><LF>. The MCU then receive automatically this data and store them in memory? Then I need to copy those data and send them on UART2 with a printf to my computer.

Am I correct?

In the CCS6 TI Resources Explorer, I found out this UART code as example. What should I adapt?

//*****************************************************************************
//
// uart_echo.c - Example for reading data from and writing data to the UART in
//               an interrupt driven fashion.
//
// Copyright (c) 2012-2016 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 2.1.3.156 of the EK-TM4C123GXL Firmware Package.
//
//*****************************************************************************

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"

//*****************************************************************************
//
//! \addtogroup example_list
//! <h1>UART Echo (uart_echo)</h1>
//!
//! This example application utilizes the UART to echo text.  The first UART
//! (connected to the USB debug virtual serial port on the evaluation board)
//! will be configured in 115,200 baud, 8-n-1 mode.  All characters received on
//! the UART are transmitted back to the UART.
//
//*****************************************************************************

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif

//*****************************************************************************
//
// The UART interrupt handler.
//
//*****************************************************************************
void
UARTIntHandler(void)
{
    uint32_t ui32Status;

    //
    // Get the interrrupt status.
    //
    ui32Status = ROM_UARTIntStatus(UART0_BASE, true);

    //
    // Clear the asserted interrupts.
    //
    ROM_UARTIntClear(UART0_BASE, ui32Status);

    //
    // Loop while there are characters in the receive FIFO.
    //
    while(ROM_UARTCharsAvail(UART0_BASE))
    {
        //
        // Read the next character from the UART and write it back to the UART.
        //
        ROM_UARTCharPutNonBlocking(UART0_BASE,
                                   ROM_UARTCharGetNonBlocking(UART0_BASE));

        //
        // Blink the LED to show a character transfer is occuring.
        //
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);

        //
        // Delay for 1 millisecond.  Each SysCtlDelay is about 3 clocks.
        //
        SysCtlDelay(SysCtlClockGet() / (1000 * 3));

        //
        // Turn off the LED
        //
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);

    }
}

//*****************************************************************************
//
// Send a string to the UART.
//
//*****************************************************************************
void
UARTSend(const uint8_t *pui8Buffer, uint32_t ui32Count)
{
    //
    // Loop while there are more characters to send.
    //
    while(ui32Count--)
    {
        //
        // Write the next character to the UART.
        //
        ROM_UARTCharPutNonBlocking(UART0_BASE, *pui8Buffer++);
    }
}

//*****************************************************************************
//
// This example demonstrates how to send a string of data to the UART.
//
//*****************************************************************************
int
main(void)
{
    //
    // Enable lazy stacking for interrupt handlers.  This allows floating-point
    // instructions to be used within interrupt handlers, but at the expense of
    // extra stack usage.
    //
    ROM_FPUEnable();
    ROM_FPULazyStackingEnable();

    //
    // Set the clocking to run directly from the crystal.
    //
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
                       SYSCTL_XTAL_16MHZ);

    //
    // Enable the GPIO port that is used for the on-board LED.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

    //
    // Enable the GPIO pins for the LED (PF2).
    //
    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);

    //
    // Enable the peripherals used by this example.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Enable processor interrupts.
    //
    ROM_IntMasterEnable();

    //
    // Set GPIO A0 and A1 as UART pins.
    //
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Configure the UART for 115,200, 8-N-1 operation.
    //
    ROM_UARTConfigSetExpClk(UART0_BASE, ROM_SysCtlClockGet(), 115200,
                            (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                             UART_CONFIG_PAR_NONE));

    //
    // Enable the UART interrupt.
    //
    ROM_IntEnable(INT_UART0);
    ROM_UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT);

    //
    // Prompt for text to be entered.
    //
    UARTSend((uint8_t *)"\033[2JEnter text: ", 16);

    //
    // Loop forever echoing data through the UART.
    //
    while(1)
    {
    }
}

[As this question is related to embedded software on MCU, I am unsure if this question should be posted in EE or in the software stack exchange. Please feel free to move my question to the right site if necessary.]

\$\endgroup\$
  • 2
    \$\begingroup\$ Don't you even look at the preview before hitting Post Your Answer? \$\endgroup\$ – pipe Sep 16 '16 at 11:22
  • \$\begingroup\$ @bence thank you for the edit. On my screen the bracket for code insertion are not appearing seems like (any idea why ?) \$\endgroup\$ – chris Sep 16 '16 at 11:33
  • \$\begingroup\$ Unfortunately not, also I just added some more tags. If you insert 4 spaces before a line then it will be code formatted. As workaround I usually copy the whole code into Notepad++, Ctrl+A, hit Tab and then copy the code into the question. This way all line has 4 spaces added. \$\endgroup\$ – Bence Kaulics Sep 16 '16 at 11:38
  • \$\begingroup\$ You can also just press Ctrl+K on a whole block in the stackexchange editor. \$\endgroup\$ – pipe Sep 16 '16 at 11:39
  • \$\begingroup\$ @BenceKaulics It should have some tag in the edit like in most BBS forum to indicate code insertion. I will insert 4 spaces before in my next questions in the future \$\endgroup\$ – chris Sep 16 '16 at 11:40
1
\$\begingroup\$

Updated answer based on comments with the OP. So this will be a bit general as I am not really familier with this MCU family.

To acquire the longitude, latitude and time the only needed NMEA senstence is the GPRMC. So first you have to catch this one in the ISR and store it in a buffer.

All sentence starts with a $, and then comes the message id, in this case GPRMC, the sequence ends with \r\n.

enter image description here

#include <string.h>

uint8_t s_buffer[200];           // sentence buffer to save GPRMC data
volatile uint8_t cntr = 0;       // buffer index

volatile uint8_t gprmc = 0;      // flag to indicate GPRMC sentence
uint8_t rdy = 0;                 // flag to indicate if the s_buffer is ready to be processed



void
UARTIntHandler(void) {
    uint32_t ui32Status;

    uint8_t _char;          // temp variable to read next byte
    uint8_t msg_id[6];      // buffer to save msg ID, which will be 5 bytes long
    uint8_t id_cntr = 6;    // buffer index

    ui32Status = ROM_UARTIntStatus(UART1_BASE, true); // Get the interrrupt status.
    ROM_UARTIntClear(UART1_BASE, ui32Status);         // Clear the asserted interrupts.

    // While there are characters in the receive FIFO.
    while(ROM_UARTCharsAvail(UART1_BASE)) {
        _char = ROM_UARTCharGetNonBlocking(UART1_BASE);

        if(gprmc) {                                  // collect sentence data
            if(_char == '\r') {                      // until '\r' is read
                cntr = 0;                            // then reset buffer index
                gprmc = 0;                           // and reset GPRMC indication
            } else {
                s_buffer[cntr++] = _char;            // store GPRMC sentence data
                rdy = 1;                             // buffer ready to be processed
            }
        }

        if(id_cntr < 6) {                            // a '$' was received
            msg_id[id_cntr++] = _char;               // collect msg ID

            if(id_cntr == 5) {
                msg_id[id_cntr] = 0;                 // add terminating null

                if(strncmp(msg_id, "GPRMC", 5) == 0){// check for GPRMC
                    gprmc = 1;                       // indicate GPRMC sentence
                }
            }
        }

        if(_char == '$'){                            // wait for '$' to capture the msg ID
            id_cntr = 0;
        }

    }
}

This will give a comma separated list containing the GPRMC data. I have simulated the GPS with static data and the UART with a for cycle.

char* test = "$GPRMC,013732.000,A,3150.7238,N,11711.7278,E,0.00,0.00,220413,,,A*68\r\n$GPVTG,0.0,T,,M,0.0,N,0.1,K,A*0C\r\n$GPGGA,015540.000,3150.68378,N,11711.93139,E,1,17,0.6,0051.6,M,0.0,M,,*58\r\n$GPGSA,A,3,14,06,16,31,23,,,,,,,,1.66,1.42,0.84*0F\r\n$GPGSV,3,1,12,01,05,060,18,02,17,259,43,04,56,287,28,09,08,277,28*77\r\n$GPGSV,3,2,12,10,34,195,46,13,08,125,45,17,67,014,,20,32,048,24*74\r\n$GPGSV,3,3,12,23,13,094,48,24,04,292,24,28,49,178,46,32,06,037,22*7D\r\n$GPGLL,3110.2908,N,12123.2348,E,041139.000,A,A*59\r\n$GPRMC,015511.000,A,3152.7238,N,11720.7278,E,0.00,0.00,220413,,,A*69\r\n";

enter image description here

This list can be easily processed, and the longitude, latitude and time be saved in the EEPROM. On the Tiva TM4C it is done something like this. The EEPROM is only 2 kB so you will have to find a way to store data efficiently, so you won't run out of space.

This is for data parsing and storage.


To send the data to a PC you should listen on UART2 for a command, which if received you could read back the data from the EEPROM (format it if you have made some modifications to store it efficiently) and use the send function to transmit the data.

\$\endgroup\$
  • \$\begingroup\$ I understand well the init of the UART on the MCU, what I have difficulties with is how to manage the parsing and the buffering of the data sent by the GPS. Currently the GPS "spit" data continuously trough UART (I can visualize this when connecting the module to my computer via USB to UART cable). Whaty I need to do is to manage with receiving, buffering and storage of the data before sending them back to the second UART. Do I understand well that to send back the data I will need to use the command "UARTSend" ? \$\endgroup\$ – chris Sep 16 '16 at 12:19
  • \$\begingroup\$ How much data do you want to receive and store on the MCU before sending it to the PC. I have to imagine a situation where the MCU collects GPS data for a day, and at the end of the day it uploads the data to the PC? \$\endgroup\$ – Bence Kaulics Sep 16 '16 at 12:36
  • \$\begingroup\$ yes you are correct about a day. I just need position longitude, latitude and time. I don't need the velocity . \$\endgroup\$ – chris Sep 16 '16 at 12:55
  • \$\begingroup\$ @chris If I will have a little free time, I will try to update my answer. Till then search some example about how can you store data in the MCU's EEPROM. \$\endgroup\$ – Bence Kaulics Sep 16 '16 at 16:57
0
\$\begingroup\$

I'm not familiar with that CPU/enviroment so I can't comment on specifics of the code.

If you read the GPS module documentation you would see at the start of section 2 that it defaults to outputting all of the NMEA messages so unless you want to change that you shouldn't need to send it any commands.

So all you need to do is:

  • Initialize both uarts. (9600 baud for the GPS, whatever you want for the PC connection)
  • Set up the UART0 interrupt routine to forward everything it gets on to UART1.

The sample code you posted currently uses 115200 baud and the interrupt routine is echoing data back to uart0, changing that to send it to uart1 instead should be as simple as changing a 0 to a 1.

The sample code also has a pause and sets LEDs, you probably want to remove those bits.

\$\endgroup\$
  • \$\begingroup\$ I already success to get data from the GPS trough UARt on computer. What I don't know how to do is this : "Set up the UART0 interrupt routine to forward everything it gets on to UART1." \$\endgroup\$ – chris Sep 16 '16 at 12:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.