Generating pulses

Cr2O7

Member
Hi!

I am currently working on project aiming at connecting fuel meters, etc to the NMEA2000-network in my boat.
A part of the work is to create a simulator for the devices on the boat. My first task is to emulate the flow meters.
They transmit pulses of which is proportional to the volume of fuel passed. The boat is equipped with two diesel engines,
so four meters are needed.

The current setup is two Teensy chips (4.0 and 4.1), of which the 4.0 is acting as simulator.
I've programmed this as four threads acting on four pins, one for each flow meter. My intention was to be able to
pulse rates between 20 - 100 Hz.However, some problem appears...

When the following rates are used, it works nicely:

Pin #2 50 Hz
Pin #3 25 Hz
Pin #5 50 Hz
Pin #6 25 Hz

The scope reports: 45 Hz and 22 Hz, which is not dead on, but reasonable for my purpose. (See attachment. The frequency is shown in the bottom left hand corner.)

However, when I run it with the following:

Pin #2 80 Hz
Pin #3 25 Hz
Pin #5 50 Hz
Pin #6 25 Hz

I get the same 45 Hz output and I am surprised. I would have anticipated something around 90 Hz here.

Am I doing something wrong, if so please enlighten me.

Rgds,
Göran



Code:
#include "JeevesIISimulatorNMEA2000.h"

/*******************************************************************************

  Function name: 
    
  Description:  
    
  Parameters:                    
    
  Return: 
 
  
  Written by:     Göran Andersson
  
   
  Date:           2021-MM-DD
    
  Version history
  
  First issue ver 1.0
*******************************************************************************/ 
/*
  	TODO
		====
		
		+	Separate the start of pulse generating threds, so maybe better performance can be acheived?






*/

bool TraceFlag   = true,
     TraceFlag2  = false,
     SyncTimePC  = true,
     TestOnly    = true,
     FuncTest    = false;

// Containing all data we need for the threads.
ThreadsDef_t FlowmeterThread[NbrOfThreads];

void setup() 
{
  if(TraceFlag
        ||
     TraceFlag2)
  {
    Serial.begin(115200);
    while(!Serial)
       delay(10);

    delay(2000);
    Serial.println("Buuu");
    Serial.print("FlowmeterHz: ");
    Serial.println(FlowmeterHz);

  }

 
  Initialise();

}

void loop() 
{
 

}


/*******************************************************************************

  Function name:   BBPrimaryFlowMeter
    
  Description:     
                   
                                       
    
  Parameters:       
                        
   
  Return:          None
  
  
  Written by:     Göran Andersson
  
  
  Date:           2021-12-20
    
  Version history
  
  First issue ver 1.0
  
*******************************************************************************/
void BBPrimaryFlowMeter(void)
{
 int State = HIGH;

 threads.delay(ThreadStartDelay + 13);  
	
	while(Forever)
	{		 
		digitalWriteFast(PrimaryBBFlowmeterPin, State); 
		State = (~State) & StateMask;
   
    if(DelayTimeBBPrimary < 30)
      threads.delay_us((1000*DelayTimeBBPrimary) >> 1);
    else
     threads.delay(DelayTimeBBPrimary >> 1);
   	
	}
	
}

/*******************************************************************************

  Function name:   BBReturnFlowMeter
    
  Description:     
                   
                                       
    
  Parameters:       
                        
   
  Return:          None
  
  
  Written by:     Göran Andersson
  
  
  Date:           2021-12-20
    
  Version history
  
  First issue ver 1.0
  
*******************************************************************************/
void BBReturnFlowMeter(void)
{
 int State = HIGH;

 threads.delay(ThreadStartDelay + 7);  
	
	while(Forever)
	{		 
		digitalWriteFast(ReturnBBFlowmeterPin, State); 
		State = (~State) & StateMask;
   
    if(DelayTimeBBReturn < 30)
      threads.delay_us((1000*DelayTimeBBReturn) >> 1);
    else
     threads.delay(DelayTimeBBReturn >> 1);
   	
	}
	
}


/*******************************************************************************

  Function name:   SBPrimaryFlowMeter
    
  Description:     
                   
                                       
    
  Parameters:       
                        
   
  Return:          None
  
  
  Written by:     Göran Andersson
  
  
  Date:           2021-12-20
    
  Version history
  
  First issue ver 1.0
  
*******************************************************************************/
void SBPrimaryFlowMeter(void)
{
	int State = HIGH;

  threads.delay(ThreadStartDelay + 5);  
	
	while(Forever)
	{		 
		digitalWriteFast(PrimarySBFlowmeterPin, State); 
		State = (~State) & StateMask;
   
    if(DelayTimeSBPrimary < 30)
      threads.delay_us((1000*DelayTimeSBPrimary) >> 1);
    else
     threads.delay(DelayTimeSBPrimary >> 1);
   	
	}
	
}

/*******************************************************************************

  Function name:   SBReturnFlowMeter
    
  Description:     
                   
                                       
    
  Parameters:       
                        
   
  Return:          None
  
  
  Written by:     Göran Andersson
  
  
  Date:           2021-12-20
    
  Version history
  
  First issue ver 1.0
  
*******************************************************************************/
void SBReturnFlowMeter(void)
{
	int State = HIGH;

  threads.delay(ThreadStartDelay + 3);  
	
	while(Forever)
	{		 
		digitalWriteFast(ReturnSBFlowmeterPin, State); 
		State = (~State) & StateMask;
   
    if(DelayTimeSBReturn < 30)
      threads.delay_us((1000*DelayTimeSBReturn) >> 1);
    else
     threads.delay(DelayTimeSBReturn >> 1);
   	
	}
	
}



/*******************************************************************************

  Function name:   Initialise
    
  Description:     
                   
                                       
    
  Parameters:       
                        
   
  Return:          None
  
  
  Written by:     Göran Andersson
  
  
  Date:           2021-12-20
    
  Version history
  
  First issue ver 1.0
  
*******************************************************************************/
void Initialise(void)
{
	
	ThreadIdx_t ThreadIdx; 
	
  FlowmeterThread[BBPrimaryThread]  = {
                                             	BBPrimaryFlowMeter,                                    
                                             	"BBPrimaryFlowMeter", 
                                             	ThreadFailed 
                                                                                                     
                                        };
                                        
  FlowmeterThread[BBReturnThread]  = {
                                             	BBReturnFlowMeter,                                    
                                             	"BBReturnFlowMeter", 
                                             	ThreadFailed 
                                             	                                                                                     
                                        }; 
                                        
  FlowmeterThread[SBPrimaryThread]  = {
                                             	SBPrimaryFlowMeter,                                    
                                             	"SBPrimaryFlowMeter", 
                                             	ThreadFailed 
                                                                                                                                                   
                                        };
                                        
  FlowmeterThread[SBReturnThread]  = {
                                             	SBReturnFlowMeter,                                    
                                             	"SBReturnFlowMeter", 
                                             	ThreadFailed 
                                                                                                                              
                                        }; 

  pinMode(PrimaryBBFlowmeterPin, OUTPUT);
  pinMode(PrimarySBFlowmeterPin, OUTPUT);   
  pinMode(ReturnSBFlowmeterPin, OUTPUT);  
  pinMode(ReturnBBFlowmeterPin, OUTPUT);                                          
                                                                                               
                                          
  StartFlowmeterThreads();                                                                                                                                                 
} 

/*******************************************************************************

  Function name:    AddFlowmeterThread
    
  Description:      The function starts a thread that is defined in the control structure 'FlowmeterThread'.
                    The outcome is sent the 'Serial' interface.
    
  Parameters:       ThreadIdx   An index of the 'FlowmeterThread' array
   
  Return:           None
  
  
  Written by:       Göran Andersson
  
  
  Date:             2021-12-20
    
  Version history
  
  First issue ver 1.0
  
*******************************************************************************/
void AddFlowmeterThread(ThreadIdx_t ThreadIdx)
{
  int TmpThreadID = threads.addThread(FlowmeterThread[ThreadIdx].Tread_F);

  if(TraceFlag)
  {
    Serial.println("####### Entry AddFlowmeterThread #######");
//    Serial.println(TmpThreadID);
//    Serial.println(JeevesIIThread[ThreadIdx].ThreadName);    
  }   
  

  if(TmpThreadID == ThreadFailed)
  {


    if(TraceFlag)
    {
      Serial.print("Failed creating thread: ");
      Serial.print(FlowmeterThread[ThreadIdx].ThreadName);
      Serial.println("! Harakiri..."); 
    }      
    exit(1);
  }
  else
  {
    	      
    if(TraceFlag)
    {    
      Serial.print("Created thread: ");       
      Serial.println(FlowmeterThread[ThreadIdx].ThreadName);
      Serial.print("  Thread state: "); 
      Serial.println(threads.getState(TmpThreadID));
      Serial.print("  Thread ID: "); 
      Serial.println(TmpThreadID);      
    }       
  }
  FlowmeterThread[ThreadIdx].ThreadID = TmpThreadID; 
 
  if(TraceFlag)
    Serial.println("####### Exit AddFlowmeterThread #######");
}

/*******************************************************************************

  Function name:    StartFlowmeterThreads
    
  Description:      The function starts all the threads
    
  Parameters:       None
   
  Return:           None
  
  
  Written by:       Göran Andersson
  
  
  Date:             2021-12-20
    
  Version history
  
  First issue ver 1.0
  
*******************************************************************************/
void StartFlowmeterThreads(void)
{
	ThreadIdx_t ThreadIdx = FirstThread;  

  while(ThreadIdx < NbrOfThreads)
  {
    AddFlowmeterThread(ThreadIdx);
    ThreadIdx = ThreadIdx_t (ThreadIdx + 1);
  }      
	
}
Code:
#include <strings.h>
//#include <stdint.h>
#include <TeensyThreads.h>

#ifndef _JEEVESII_SIMULATOR_NMEA2000_H
#define _JEEVESII_SIMULATOR_NMEA2000_H

#define MaxTicks              											4294967295 // + 1 -> 0 

#define PrimaryBBFlowmeterPin 											2
#define ReturnBBFlowmeterPin  											3
#define PrimarySBFlowmeterPin 											5
#define ReturnSBFlowmeterPin  											6 

#define ThreadFailed            										-1
#define FlowmeterHz                                 50  // 83 OK, but not 84!    						 // Hz

// Pulse period
#define DelayTimeBBPrimary                          20 //10      // ms   
#define DelayTimeBBReturn                           40       // ms    
#define DelayTimeSBPrimary                          20       // ms    
#define DelayTimeSBReturn                           40       // ms    
#define ThreadStartDelay                            1000   

#define StateMask                                   1

typedef enum 
{
	
	FirstThread, BBPrimaryThread = FirstThread, BBReturnThread, SBPrimaryThread, SBReturnThread, NbrOfThreads
	
}ThreadIdx_t; 

typedef struct ThreadsDef
{
	
	ThreadFunctionNone Tread_F;
	char *ThreadName;
	int  ThreadID;

}ThreadsDef_t;


void BBPrimaryFlowMeter(void);

void BBReturnFlowMeter(void);

void SBPrimaryFlowMeter(void);

void SBReturnFlowMeter(void);

void AddFlowmeterThread(ThreadIdx_t ThreadIdx);

void StartFlowmeterThreads(void);

void Initialise(void);

static bool Forever = true;
    
// #include "JeevesIINMEA2000.h"      
#endif
 

Attachments

  • Screenshot from 2021-12-23 21-11-42.jpg
    Screenshot from 2021-12-23 21-11-42.jpg
    68.8 KB · Views: 33
I don't know anything about your threading library, but if you only need to generate a few frequencies on pins you can do this:
Code:
include "Arduino.h"

void setup()
{
    analogWriteFrequency(1, 80);  // 80Hz on pin 1
    analogWrite(1, 128);          // 50% duty cycle

    analogWriteFrequency(3, 25);
    analogWrite(3, 128);

    analogWriteFrequency(5, 50);
    analogWrite(5, 128);

    analogWriteFrequency(6, 25);
    analogWrite(6, 128);
}

void loop(){
}

Screenshot 2021-12-24 112805.jpg

Please note that a few pins share the same PWM timer, e.g. pin 2 and pin 3. You need to make sure that your pins don't share the same timers. I therefore changed you pin2 to pin1. You can find out which pins share the same timer here https://www.pjrc.com/teensy/td_pulse.html Scroll down to the PWM Frequency table.
 
Back
Top