Charles Young's development fork. Going forward I only want to push mature code to main repository.

Dependencies:   mbed

Fork of GEO_COUNTER_L432KC by Geo Electronics "Geo Counter"

main.cpp

Committer:
Charles David Young
Date:
2018-09-03
Revision:
16:b2e77eb76ab4
Parent:
15:808e98afe32b
Child:
17:6eed17197004

File content as of revision 16:b2e77eb76ab4:

//  GEO COUNTER V1 firmware
//  This FW provides a basic operation of GEO-COUNTER
//
//  Latest review: August 27, 2018 - Walter Trovo
//
//  Feb 14, 2018: initial release aimed to test the counters, the serial port
//                the PWM output and the MAX7219 operation.
//  Feb 15, 2018: Removed MAX7219 libray (replaced with custom routine). 
//                Added 74HC595 routine. Added beep. Added Gate_write
//


// this block includes key libraries
#include "mbed.h"       // global Mbed library (always needed)
#include <string>       // strings management
#include "QEI.h"        // Quadrature Encoder functions

// definitions of fixed parameters

#define DEC_MODE    0x09FF  // BCD decoding on all digits
#define BRIGHTNESS  0x0A0F  // max brightness
#define SCAN_LIM    0x0B07  // use all 8 digits  
#define TURN_ON     0x0C01  // no shutdown (operating)
#define SHUTDOWN    0x0C00  // shutdown
#define TEST        0x0F00  // no test

#define DT      1      // delay time in us for SPI emulation

#define TGATE   10       // gate time (currently fixed for testing purpose)
#define MAX_VAL 999999  // Max value managed by the 6-digits display


#define CPM     0x01
#define CPS     0x02
#define PLS     0x04
#define VOLTS   0x08
#define CNT1    0x10
#define CNT2    0x20
#define HV      0x40
#define MENU    0x80
uint8_t LED_statuses[] = {CPM, CPS, PLS, VOLTS, CNT1, CNT2, HV, MENU};
uint8_t LED_status = CPM;
uint8_t LED_status_index = 0;

// definitions of the input/outputs (pins)
DigitalOut  AUX   (D2);    // AUX control for GPS module
InterruptIn TRIG1 (D3);    // Counter 1 trigger
InterruptIn TRIG2 (D6);    // Counter 2 trigger
DigitalIn   QEPB  (D9);    // Quadrature encoder pushbutton
PwmOut      PWM   (D10);   // PWM output
DigitalOut  BUZZ  (D13);   // Buzzer

AnalogIn    AIN0  (A0);    // ADC input 0 (High Voltage)
AnalogIn    AIN1  (A1);    // ADC input 1 (aux)
DigitalOut  CS2   (A2);    // 74HC595 RCLK (pin 12)
DigitalOut  CS1   (A3);    // MAX7219 CS (pin 12)
DigitalOut  SCK   (A4);    // 74HC595 SRCLK (pin 11) & MAX7219 SCK (pin 13)
AnalogIn    KEYB  (A5);    // Keyboard input (SW2 & SW3) 
DigitalOut  MOSI  (A6);    // 74HC595 SER (pin 14) & MAX7219 DIN (pin 1)
DigitalIn   UN    (A7);    // Unused (in V1 PCB A5 and A7 must be connected)

// LED on processor board
DigitalOut led1(LED1);

// definitions of peripherals and devices

// QEI class supports the mode wheel.  In its most basic function it will tell
// us which direction the wheel is moving (right or left) so that we can
// use it to select the current mode.
QEI     Wheel(D12, D11, NC, 16);    // Quadrature encoder
int WheelCurrent = 0;
int WheelPrevious = 0;
bool QEPBpressed = false; // only react to button when pressed
enum WheelState {
   WHEEL_INACTIVE       = 0,
   WHEEL_MODE_SELECT    = 1,
   WHEEL_SUBMODE_SELECT = 2
};
enum WheelStateEvent {
   WHEEL_Pressed = 0,
   WHEEL_Timeout = 1
};
WheelState currentWheelState;

I2C     i2c(D4, D5);                // I2C port
Ticker  Sec_Beat;                // 1 second ticker
Serial  PC(USBTX, USBRX);        // Virtual COM via USB (PC connection)
Serial  GPS(D1, D0);             // Serial port for GPS module

// Global variables
uint8_t     Disp_Digit[8]; // used to manage 8-digits through MAX7219
uint16_t    Stream;        // used to stream out serial data to MAX7219
time_t      seconds;       // Real-Time Clock (RTC) timestamp
unsigned int value = 0;    // displayed value on the 6-digits of the display
uint8_t     gate = TGATE;          // displayed value on the 2-digits display
uint32_t    Count1, Count2;    // pulse counters (32-bit)
char        Text[40]="";   // used to send messages over the serial port
uint8_t     Disp_mode = 0x01, Disp_unit = 0xA0;   // status of 1st row and 2nd rows of LEDs
bool        Stopped = 0;        // status of counting activity
bool        StartStopPressed = 0;// status of counting activity
double      ADC_val;    // used to read ADC value

// ----- Prototypes of routines (defined below the main) -------------------
void UpdateInput(void);  // periodically called by the ticker
void UpdateOutput(void);  // periodically called by the ticker
void Count1_up(void);  // called every time an edge is detected on TRIG1 pin
void Count2_up(void); // called every time an edge is detected on TRIG2 pin
void Beep(void);    // used to generate a short beep (buzzer)
void LEDs_write(unsigned short);    // write to 74HC595 (8x LEDs)
void Display_init(void);    // initialize MAX7219
void Display_6D_write(uint8_t);    // write to MAX7219 (Main 6-digits display)
void Display_2D_write(unsigned short);    // write to MAX7219 (Gate 2-digits display)

//==============================================================================
//==============================================================================

int main() 
{
    
    PC.baud(115200);        // set baud-rate of virtual COM port (PC connection)
    PC.printf("\nGEO COUNTER V1 2108");
    PC.printf(__DATE__);
    PC.printf("  ");
    PC.printf(__TIME__);
    
    GPS.baud(9600); // set the baud-rate of the serial port dedicated to the GPS
    
    CS1 = 1;    // presets CS of MAX7219
    CS2 = 1;    // preset CS of 74HC595

    Display_6D_write(0x543210);
    Display_2D_write(TGATE);
    Display_init(); // initialize MAX7219

    // RTC is supposed to be loose time at power down (no backup battery)
    // An initialization is performed anyway 
    set_time(0); // Set time
 
    Wheel.reset();        // clear the variable associated to the encoder    
    
    PWM.period_ms(3);    // set the PWM period
    PWM.write(0.8);      // set the PWM duty-cycle
    
    LEDs_write(0x00);   // initialize LEDs (CPM and CNT1 on)
    Beep(); // initial beep    
    
    // set the 1 sec ticker to periodically call the Update() routine
    // NOTE: this is also the 1-sec time base for counters. A better approach
    // would replace the ticker with an interrupt from the RTC (to be implemented)
    Sec_Beat.attach_us(&UpdateInput,  100000);  
    Sec_Beat.attach_us(&UpdateOutput, 1000000);  
    //RTC::attach(&Update, RTC::Second);
    //RTC::detach(RTC::Second);  
  
    // main loop does nothing as all activities are interrupt driven    
    while(1) 
    {
        // dance (or drink a beer)                
    }
}


//-------- END OF MAIN --------------
//==============================================================================

// Definition of routines

//---------------------------------------------------------------------------
// Update values to be displayed  
void logToPC()
{               
   PC.printf("\nADC: %.02f", ADC_val); 
   PC.printf(Stopped ? " stopped" : " started");
   // Timestamp to PC (debug)
   seconds = time(NULL);   // get current time 
   strftime(Text, 50, "%H:%M:%S", localtime(&seconds));
   PC.printf(" RTC: %s, CNT1: %7d CNT2: %7d",Text, Count1, Count2);
   PC.printf(" wheel %d %d", WheelCurrent, QEPB.read());
}

void UpdateOutput()   
{               
   if(Stopped)
   {    
      // disable interrupts on TRIG1 and TRIG2
        
      TRIG1.rise(NULL);      
      TRIG2.rise(NULL); 
        
      // show zero gate time   
      gate = 0;                   
      Display_2D_write(gate);
        
      // show selected content on main display
      value = (int)(Count1/TGATE);
      Display_6D_write(value);    // refresh the main display              
   }
    
   else
   {
      // Enable interrupts on rising edge of digital inputs TRIG1 & TRIG2
      TRIG1.rise(&Count1_up);     
      TRIG2.rise(&Count2_up);         
        
      if(gate==0) // show the counter value at the end of the gate time
      {
         value = (int)(Count1/TGATE);
                
         Display_6D_write(value);    // refresh the main display 
        
         Count1 = 0;     // clear both counters
         Count2 = 0;
         gate = TGATE;// and reload the gate time

      }

      Display_2D_write(gate);     // show gate time countdown 
      gate--;
   }
}

void UpdateInput()   
{               
   LEDs_write(LED_statuses[LED_status_index]);
        
   ADC_val = KEYB.read();  // read voltage from keyboard
   if (   (ADC_val<0.1)    // START/STOP pushbutton pressed
       && (!StartStopPressed))
   {
      StartStopPressed = true;
      Stopped=!Stopped;           // toggle status
   }
   else
      StartStopPressed = false;
    
   if((ADC_val>0.6)&&(ADC_val<0.7))    // CLEAR pushbutton pressed
   { 
      Count1 = 0; // clear counters     
      Count2 = 0;              
   }

   if (   (WHEEL_MODE_SELECT    == currentWheelState)
       || (WHEEL_SUBMODE_SELECT == currentWheelState))
   {
      WheelCurrent      = int(Wheel.getPulses());
      if (WheelCurrent > WheelPrevious)
         LED_status_index = ++LED_status_index % sizeof(LED_statuses);
      else
         if (WheelCurrent < WheelPrevious)
            LED_status_index = --LED_status_index % sizeof(LED_statuses);
      WheelPrevious = WheelCurrent;
   }

   // detect when wheel button is pressed but wait until it is released
   // before doing anything
   bool QEPBbutton = QEPB.read();
   if (   (QEPBbutton)
       && (!QEPBpressed))
   {
      QEPBpressed = true;
   }
   else
      if (   (!QEPBbutton)
          && (QEPBpressed))
      {
         QEPBpressed = false;
         WheelStateMachine(WHEEL_Pressed);
      }
    
   logToPC();
   return;
}

void WheelStateMachine(WheelStateEvent event)
{                       
   switch currentWheelState
   {
      WHEEL_INACTIVE:
      if (WHEEL_Pressed == event)
         currentWheelState = WHEEL_MODE_SELECT;
      break;
      WHEEL_MODE_SELECT:
      if (WHEEL_Pressed == event)
         currentWheelState = WHEEL_SUBMODE_SELECT;
      else
         if (WHEEL_Timeout == event)
            currentWheelState = WHEEL_INACTIVE;
      break;
      WHEEL_SUBMODE_SELECT:
      if (WHEEL_Pressed == event)
         currentWheelState = WHEEL_MODE_SELECT;
      else
         if (WHEEL_Timeout == event)
            currentWheelState = WHEEL_INACTIVE;
      break;
      default:
   }
}

//---------------------------------------------------------------------------
// Increment CNT1 every time a rising edge is detected on TRIG1 (interrupt)

void Count1_up(void)
{                       
    Count1++;                        
    return; 
}


//---------------------------------------------------------------------------
// Increment CNT1 every time a rising edge is detected on TRIG2 (interrupt)

void Count2_up(void)
{                      
    Count2++;                        
    return; 
}


//---------------------------------------------------------------------------
//Generates a short beep via BUZZ

void Beep(void)       
{                       
    BUZZ = 1;         // turn-on the buzzer
    wait(0.3);        // wait
    BUZZ = 0;         // turn-off the buzzer   
    return; 
}


//---------------------------------------------------------------------------
//Write to 74HC595 (LEDs) - Take care to avoid conflict with MAX7219

void LEDs_write(unsigned short data_val)       
{                       
    // Update 74HC595 shift registers 
    unsigned short mask;
    
    SCK = 0;
    wait_us(DT);
    CS2 = 0;    
    
    for(mask = 0x80; mask!= 0; mask>>= 1)
    {
      wait_us(DT);
      SCK = 0;
      if(mask & data_val)
        MOSI = 0;
      else
        MOSI = 1;
      wait_us(DT);
      SCK = 1;
    }
    
    SCK = 0;
    wait_us(DT);
    CS2 = 1;

    return; 
}


//---------------------------------------------------------------------------
// Initialize the MAX7219

void Display_init(void)
{
    uint8_t i;
    uint16_t mask;
    uint16_t data_to_send[6] = {SHUTDOWN, TURN_ON, DEC_MODE, BRIGHTNESS, SCAN_LIM, TEST};
             //{SHUTDOWN, TURN_ON, DEC_MODE, BRIGHTNESS, SCAN_LIM, TEST};
    for(i = 0; i <6; i++)
    {
        CS1 = 0;
      
        for(mask = 0x8000; mask!= 0; mask>>= 1)
        {
            wait_us(DT);
            SCK = 0;
            if(mask & data_to_send[i])
                MOSI = 1;
            else
                MOSI = 0;
            wait_us(DT);    
            SCK = 1;
        }
        
        wait_us(DT);
        SCK = 0;
        wait_us(DT);
        CS1 = 1;
    }
    
  return;
}


//---------------------------------------------------------------------------
// Refresh the 6 digits of the main display

void Display_6D_write(uint8_t value)
{
 
    uint8_t digit;
    uint16_t mask, data_to_send;
    char TextString[6];
    
    // int to string, then string to digits
    
    sprintf(TextString, "%6d", value);   // int to string
    
    for(uint8_t i=0; i<6; i++)
    {   
        if(TextString[i] == ' ')  // blank empty digits
            Disp_Digit[i] = 0xFF;
        else
            Disp_Digit[i] = TextString[i]-'0';      
    }

    // write to chip

    SCK = 0;
    wait_us(DT);    
    
    for(digit = 1; digit <7; digit++)
    {
        // each stream consists of digit address and data to show
        data_to_send = 7 - digit;
        data_to_send<<=8;
        data_to_send = data_to_send | Disp_Digit[digit-1];
        
        CS1 = 0;
        
        for(mask = 0x8000; mask!= 0; mask>>= 1)
        {
            wait_us(DT);
            SCK = 0;
            if(mask & data_to_send)
                MOSI = 1;
            else
                MOSI = 0;
                
            wait_us(DT);
            SCK = 1;
        }
        
        wait_us(DT);    
        SCK = 0;
        wait_us(DT);
        CS1 = 1;
    }
    
  return;
}


//---------------------------------------------------------------------------
// Refresh the 2 digits of the gate display

void Display_2D_write(unsigned short value)
{
 
    uint8_t digit;
    uint16_t mask, data_to_send;
    char TextString[2];

    // int to string, then string to digits
    
    sprintf(TextString, "%2d", value);   // int to string

    if(TextString[0] == ' ')  // blank empty digits
        Disp_Digit[7] = 0xFF;    
    else
        Disp_Digit[7] = TextString[0] - '0';
    
    Disp_Digit[6] = TextString[1] - '0';

    // write to chip
    
    SCK = 0;
    wait_us(DT);    
    
    for(digit = 7; digit <9; digit++)
    {
        // each stream consists of digit address and data to show
        data_to_send = digit;
        data_to_send<<=8;
        data_to_send = data_to_send | Disp_Digit[digit-1];
        
        CS1 = 0;
        
        for(mask = 0x8000; mask!= 0; mask>>= 1)
        {
            wait_us(DT);
            SCK = 0;
            
            if(mask & data_to_send)
                MOSI = 1;
            else
                MOSI = 0;
                
            wait_us(DT);
            SCK = 1;
        }
        
        wait_us(DT);    
        SCK = 0;
        wait_us(DT);
        CS1 = 1;
    }
    
  return;
}

//-------- END OF FILE --------------
//==============================================================================