Accurate Frequency Counter up to 25MHz. Base clock is compensated by GPS 1PPS pulse. This program runs only on mbed NucleoF411RE.

Dependencies:   ADT7410 CheckRTC DRV8830 Frq_cuntr_full PID TextLCD mbed-rtos mbed iSerial

Fork of Frequency_Counter by Kenji Arai

Please refer following.
/users/kenjiArai/notebook/frequency-counters/

main.cpp

Committer:
kenjiArai
Date:
2015-01-02
Revision:
12:05098414599b
Parent:
11:20e7a45f1448

File content as of revision 12:05098414599b:

/*
 * mbed Application program / Frequency Counter with GPS 1PPS Compensation
 *
 * Copyright (c) 2014 Kenji Arai / JH1PJL
 *  http://www.page.sannet.ne.jp/kenjia/index.html
 *  http://mbed.org/users/kenjiArai/
 *      Created: October   18th, 2014
 *      Revised: January    2nd, 2015
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#define USE_COM         // use Communication with PC(UART)
#define SET_RTC
//#define USE_DEBUG

//  Include ---------------------------------------------------------------------------------------
#include "mbed.h"
#include "rtos.h"
#include "TextLCD.h"
#include "DRV8830.h"
#include "ADT7410.h"
#include "PID.h"
#include "frq_cuntr_full.h"
#include "CheckRTC.h"
#include "iSerial.h"
#include "gps.h"

using namespace Frequency_counter;

//  Definition ------------------------------------------------------------------------------------
#ifdef USE_COM
#define BAUD(x)         pc.baud(x)
#define GETC(x)         pc.getc(x)
#define PUTC(x)         pc.putc(x)
#define PRINTF(...)     pc.printf(__VA_ARGS__)
#define READABLE(x)     pc.readable(x)
#else
#define BAUD(x)         {;}
#define GETC(x)         {;}
#define PUTC(x)         {;}
#define PRINTF(...)     {;}
#define READABLE(x)     {;}
#endif

#ifdef USE_DEBUG
#define DEBUGBAUD(x)    pc.baud(x)
#define DEBUG(...)      pc.printf(__VA_ARGS__)
#else
#define DEBUGBAUD(x)    {;}
#define DEBUG(...)      {;}
#endif

#define CLOCK_BASE      (24.999982f)

#define GSP_BUF_B       (128 * 3)
#define GPS_BUF_S       (128 * 2)

//  Object ----------------------------------------------------------------------------------------
DigitalOut  led_gate(LED1);
//Serial      pc(USBTX, USBRX);
iSerial     pc(USBTX, USBRX, 1024, 32);
//Serial      gps(NC, PA_10); // GPS RX
iSerial     gps(PA_9, PA_10, 32, 1024); // GPS RX
I2C         i2cBus(PB_9,PB_8);  // SDA, SCL
DRV8830     heater(i2cBus, (uint8_t)DRV8830ADDR_00);
ADT7410     t(i2cBus, ADT7410ADDR_NN);
TextLCD     lcd(PB_0, PA_4, PC_0, PC_1, PC_2, PC_3, TextLCD::LCD20x4); // rs, e, d4-d7
PID         pid(14.0f, 50.0f, 1.5f, 1.0f);
// PC_6,PC_7 & PB_6 use for Timer3 & 4(16+16bit)
// PA_0,PA_1 & PB_10 use for Timer2(32bit)
// PA_8 & PC_7 use for MCO (Test purpose)
FRQ_CUNTR   fc(PC_6, 1.0, CLOCK_BASE); // Input port, gate time[sec] and External clock freq.

//  RAM -------------------------------------------------------------------------------------------
// Freq.
uint32_t    counter_1pps;
double      new_frequency;
double      base_clock_1pps;
// Temp Control
float       volt = 0.0;
float       tmp = 0;
float       pid_val = 0;
uint32_t    n = 0;
// GPS
uint32_t    gps_status;
uint8_t     gps_rmc_ready;
char        GPS_Buffer[GSP_BUF_B];       //GPS data buffer
char        MsgBuf_RMC[GPS_BUF_S];
char        MsgBuf_GGA[GPS_BUF_S];
// Time
time_t      seconds;
time_t      seconds_jst;
struct tm   t_gps;
// rtos
Queue<uint32_t, 2> queue0;

//  ROM / Constant data ---------------------------------------------------------------------------
//                              12345678901234567890
static char *const msg_clear = "                    ";
static char *const msg_msg0  = "Frequency Counter   ";
static char *const msg_msg1  = "  mbed Nucleo F411RE";
static char *const msg_msg2  = "    by JH1PJL K.Arai";
static char *const msg_msg3  = "    "__DATE__" ";

//  Function prototypes ---------------------------------------------------------------------------
uint32_t check_gps_ready(void);
void get_time_and_date(struct tm *pt);
void getline_gps(void);

//-------------------------------------------------------------------------------------------------
//  Control Program
//-------------------------------------------------------------------------------------------------
void measure_freq(void const *args)
{
    while(true) {
        if (fc.status_freq_update() != 0) {
            new_frequency = fc.read_freq_data();
            queue0.put((uint32_t*)1);
        }
        if (fc.status_1pps() != 0) {
            counter_1pps = fc.read_avarage_1pps();
        }
        Thread::wait(100);
    }
}

void temp_control(void const *args)
{
    uint32_t error_count = 50;

    t.set_config(OPERATION_MODE_CONT + RESOLUTION_16BIT);
    pid.setInputLimits(0.0, 4.5);
    pid.setOutputLimits(0.0, 5.0);
    pid.setSetPoint(2.5);
    while(true) {
        tmp = t.read_temp();
        pid.setProcessValue(tmp / 10);
        pid_val = pid.compute();
        volt = pid_val - 2.5f;
        if (volt < -0.8f) {
            volt = -0.8f;
        } else if (volt > 0.8f) {
            volt = 0.8f;
        }
        if ((volt == -0.800f) || (volt == 0.800f)){
            if (--error_count == 0){
                pid.reset();
                error_count = 50;
            }
        } else {
            error_count = 50;
        }
        heater.set_voltage(volt);
        if (heater.status()) {
            heater.reset();
        }
        /* Wait until it is time to check again. */
        Thread::wait(1000);
    }
}

// Get GAA data
void gps_data_rcv(void const *args)
{
    int8_t i;
    time_t old_ave_sec[2];
    uint32_t diff;

    gps.baud(9600);
    seconds = time(NULL);
    DEBUG("\r\nCurrent Time: %s\r\n", ctime(&seconds));
    old_ave_sec[0] = 0;
    old_ave_sec[1] = 0;
    while(true) {
        getline_gps();          // Get GPS data from UART
        if (strncmp(GPS_Buffer, "$GPRMC",6) == 0) {
            for (i=0; GPS_Buffer[i] != 0; i++) {// Copy msg to RMC buffer
                MsgBuf_RMC[i] = GPS_Buffer[i];
            }
            MsgBuf_RMC[i+1] = 0;
            DEBUG("GET RMC\r\n");
            if (gps_status > 3) {
                get_time_and_date(&t_gps);
                seconds = mktime(&t_gps);
                if (old_ave_sec[0] == 0){
                    old_ave_sec[0] = seconds;
                } else if (old_ave_sec[1] == 0){
                    old_ave_sec[1] = seconds;
                } else {
                    if (old_ave_sec[0] >= old_ave_sec[1]){
                        diff = old_ave_sec[0] - old_ave_sec[1];
                    } else {
                        diff = old_ave_sec[1] - old_ave_sec[0];
                    }
                    if (diff > 100 ){
                        old_ave_sec[0] = seconds;
                        old_ave_sec[1] = 0;
                    } else {
                        if (old_ave_sec[0] > old_ave_sec[1]){
                            diff = seconds - old_ave_sec[0];
                            if (diff < 100){
                                old_ave_sec[1] = seconds;
                            }
                        } else {
                            diff = seconds - old_ave_sec[1];
                            if (diff < 100){
                                old_ave_sec[0] = seconds;
                            }
                        }
                        set_time(seconds);
                        DEBUG("SET RTC\r\n");
                    }
                }
            }
        } else if (strncmp(GPS_Buffer, "$GPGGA",6) == 0) {
            for (i=0; GPS_Buffer[i] != 0; i++) {// Copy msg to GGA buffer
                MsgBuf_GGA[i] = GPS_Buffer[i];
            }
            MsgBuf_GGA[i+1] = 0;
            DEBUG("GET GGA\r\n");
            gps_status = check_gps_ready();
        }
    }
}

void display_data(void const *args)
{
    char buf[40];

    BAUD(9600);
    DEBUGBAUD(9600);
    PRINTF("\r\nFrequency Counter by JH1PJL created on "__DATE__"\r\n");
    // Initial screen
    lcd.locate(0, 0);
    lcd.printf(msg_msg0);
    lcd.printf(msg_msg1);
    lcd.printf(msg_msg2);
    lcd.printf(msg_msg3);
    Thread::wait(3000);
    lcd.locate(0, 0);
    lcd.printf(msg_clear);
    lcd.printf(msg_clear);
    lcd.printf(msg_clear);
    lcd.printf(msg_clear);
    while(true) {
        osEvent evt = queue0.get();
        PRINTF("FRQ= %11.1f Hz, ", new_frequency);
        PRINTF("1PPS= %9d , %9d (new), ",counter_1pps, fc.read_newest_1pps());
        PRINTF("%+6.3f dC, ", tmp);
        PRINTF("PID= %+6.3f , ", volt);
//        PRINTF("t= %5d S, ", n++);
        seconds = time(NULL);
        seconds_jst = seconds + 32400;   // +9 hours ->JST
        //                 December 31,'14, 13:12:11
//        strftime(buf, 40, "%B %d,'%y, %H:%M:%S", localtime(&seconds));
        //                 31 Dec 2014 13:12:11
//        strftime(buf,40, "%x %X ", localtime(&seconds));
        //                 1:12:11 PM (2014/12/31)
        strftime(buf,40, "%I:%M:%S %p %m/%d", localtime(&seconds_jst));
        PRINTF("%s, ", buf);
        if (fc.status_gps()) {
            PRINTF("RDY");
        } else {
            PRINTF("NO ");
        }
        PRINTF("\r\n");
        lcd.locate(0, 0);
        lcd.printf("Freq= %11.2f Hz", new_frequency);
        lcd.locate(0, 1);
        lcd.printf("1PPS=%9d Hz", counter_1pps);
        lcd.locate(0, 2);
        strftime(buf,40, " %I:%M:%S %p (%m/%d)", localtime(&seconds_jst));
        lcd.printf("%s, ", buf);
        lcd.locate(0, 3);
        lcd.printf("t= %+4.1f%cC", tmp, 0xdf);
        if (fc.status_gps()) {
            lcd.printf("   GPS RDY");
        } else {
            lcd.printf("   NO GPS ");
        }
        base_clock_1pps = (double)counter_1pps / 1000000;
        double diff = base_clock_1pps - CLOCK_BASE;
        if (diff < 0) {
            diff *= -1;
        }
        if (diff < 0.00001) {   // less than 10Hz
            fc.set_external_clock(base_clock_1pps);
        }
        /* Wait until it is time to check again. */
        Thread::wait(100);
    }
}

// Thread definition
osThreadDef(measure_freq, osPriorityNormal,1024);
osThreadDef(temp_control, osPriorityNormal,1024);
osThreadDef(display_data, osPriorityNormal,1536);
osThreadDef(gps_data_rcv, osPriorityNormal,1536);

int main()
{
//    PA8 & PC9 uses for MCO_1 & MCO_2 -> Clock output for checking
    fc.port_mco1_mco2_set(4);      // Clk/4 ->1/1(100MHz) cannot measure!!
    osThreadCreate(osThread(measure_freq), NULL);
    Thread::wait(5);   //wait
    osThreadCreate(osThread(temp_control), NULL);
    Thread::wait(8);   //wait
    osThreadCreate(osThread(display_data), NULL);
    Thread::wait(10);   //wait
    if (CheckRTC() == OK) { // If RTC is NG, no need to start GPS RX function
        DEBUG("\r\nGPS task starts\r\n");
        osThreadCreate(osThread(gps_data_rcv), NULL);
    }
    while(true) {
        /* Wait until it is time to check again. */
        Thread::wait(5000);
    }
}

///////////////////////////////////////////////////////////////////////////////
// GPS data Analysis
//
//  PA6C GPS Module
//      http://www.gtop-tech.com/en/product/MT3339_GPS_Module_03.html
//      Update time: 1 second (1PPS: ±10ns RMS)
//      MediaTek MT3339 Chipset, L1 Frequency, C/A code, 66 Channels
//      NMEA 0183
//
//      current setting: GGA, VTG, GGA, RMC
//
///////////////////////////////////////////////////////////////////////////////
// Get line of data from GPS
void getline_gps(void)
{
    while (gps.getc() != '$') {
        //DEBUG("GETC but not $\r\n");
        Thread::yield();
    }
    GPS_Buffer[0] = '$';
    for (int8_t i = 1; i < GSP_BUF_B; i++) {
        GPS_Buffer[i] = gps.getc();
        if (GPS_Buffer[i] == '\r' || GPS_Buffer[i] == '\n') {
            GPS_Buffer[i] = '\r';
            GPS_Buffer[i+1] = '\n';
            GPS_Buffer[i+2] = 0;
            GPS_Buffer[i+3] = 0;
            //DEBUG("Get one GPS data\r\n");
            return;
        }
    }
}

// ASCII to hex
uint8_t hex(char c)
{
    if(c<= '/') return( ERR );
    if(((c -= '0')<= 9 || 10 <= ( c -= 'A' - '0' - 10)) && c <= 15) {
        return((uint8_t)c);
    }
    return(ERR);
}

// Search next ',' (comma)
char *next_comma( char *p )
{
    while (1) {
        if ( *p== ',' ) {
            return ++p;
        } else if ( *p == 0 ) {
            return (char*)-1;
        } else {
            p++;
        }
    }
}

// 2 digits ASCII char to one byte hex
unsigned char   change_acii_hex( char*p )
{
    unsigned char c;

    c = hex(*p++);              /* No concern ERR condition! */
    c = (c * 10) + hex(*p++);
    return c;
}

// Skip number of comma
char *num_of_comma( char *p, int8_t n )
{
    for (; n> 0; n--) {
        if ( (p = next_comma(p)) == (char*) -1 ) {
            return (char*)-1;
        }
    }
    return p;
}

// Get GPS status and number of satelites
uint32_t check_gps_ready(void)
{
    char *p;
    uint32_t x;
    uint8_t d;

    // pick-up time
    p = MsgBuf_GGA;
    p = num_of_comma(p,6);  // skip ','
    // reach to "Position Fix Indicator"
    if (*p == '0') {
        x = 0;
    } else if (*p == '1') {
        x = 0x10;
    } else if (*p == '2') {
        x = 0x20;
    } else {
        x= 0;
    }
    ++p;
    if (*p == ',') {
        ++p;
        d = hex(*p++);
        if (d != ERR) {
            x += (uint32_t)d;
            if (*p != ',') {
                x = 0;
            }
        } else {
            x = 0;
        }
    } else {
        x = 0;
    }
    return x;
}

//  Get time(UTC) from GPS data
void get_time_and_date(struct tm *pt)
{
    char *p;

    p = MsgBuf_RMC;
    p = num_of_comma(p,1);  /* skip one ',' */
    pt->tm_hour   = change_acii_hex(p);
    p += 2;
    pt->tm_min = change_acii_hex(p);
    p += 2;
    pt->tm_sec = change_acii_hex(p);
    p = MsgBuf_RMC;
    p = num_of_comma(p,9);  /* skip one ',' */
    pt->tm_mday  = change_acii_hex(p);
    p += 2;
    pt->tm_mon = change_acii_hex(p) - 1;
    p += 2;
    pt->tm_year  = change_acii_hex(p) + 100;
}