Electrocardiography simulator
I see a number of things that might help you improve your code.

Arrange functions from simple to compound

Within your source code files, try to arrange your functions such that simpler functions, such as checkCrossing precede compound functions such as analyzeNewValue that call other simple functions.  Doing so allows you to avoid having to write function declarations that would otherwise be required.

Use const where practical

The checkCrossing function is passed a pointer to an ekgDataModel and the function does not alter that structure.  This should be made explicit by declaring the parameter const as in:

int checkCrossing(const struct ekgDataModel* ekgData) {


Carefully consider your data

The checkCrossing routine is looking for zero-crossings and uses this expression:

if (ekgData-&gt;newValue * ekgData-&gt;previousValue &lt; 0) { /* ... */ }


However, consider the possibility that one of the samples is exactly 0.  For example, a sequence might be -0.15, 0.0, +0.20.  For (-0.15, 0.0) the expression is false and for (0.0, +0.20) the expression is again false.  The effect is that the function has just missed an obvious zero-crossing. An alternative approach would be to use the signbit macro:

if (signbit(ekgData-&gt;newValue) != signbit(ekgData-&gt;previousValue)) { /* ... */ }


I'd rewrite the function using stdbool.h as a bool function:

bool isZeroCrossing(float prev, float curr) {
    return (bool)signbit(curr) != (bool)signbit(prev);
}


Prefer &gt;= to == to prevent "runaway data"

In the checkIfFrequencyValid function, the program currently does this:

if (ekgData-&gt;amountOfData == MAX_AMOUNT_OF_DATA
    &amp;&amp; (ekgData-&gt;frequency &lt; MIN_FREQUENCY_ALLOWED
            || ekgData-&gt;frequency &gt; MAX_FREQUENCY_ALLOWED)) {
     /* stuff */
     ekgData-&gt;amountOfData = 0;
}


There is a potential problem here in that the analyzeNewValue function only increments amountOfData, so if it ever gets to MAX_AMOUNT_OF_DATA and the frequency is within range, subsequent readings will increase amountOfData beyond MAX_AMOUNT_OF_DATA which would make for a difficult-to-debug error.  Instead, use the &gt;= operator for the first clause to make sure that you don't have "runaway data" problems.

Use better names

Function names such as displayWrongFrequencyMessage and initEKGData are good because they give a very good idea of what the functions are doing just by the name.  However functions like checkCrossing and checkIfFrequencyValid are not so good.  In one case, essentially a boolean value is returned indicating that a zero crossing has occurred, but in the other, nothing is returned at all.  Better for boolean functions is to name them to suggest what the return value might mean.  For example isZeroCrossing would return true if it detected a zero crossing.  Very clear and doesn't require much further explanation.

Check return values of standard functions

Many standard functions, including scanf can fail and return values that can help in detecting those failures.  To write robust software, you should get into the habit of checking the return values and dealing intelligently with the potential failures.

Avoid scanf if you can

There are so many well known problems with scanf that you're usually better off avoiding it.  The usual approach is to read in user input into a string and then parse from there, in this case using something like atof().

Think carefully about signed versus unsigned

Would it ever be possible that amountOfData or frequency would be negative numbers?  If so, what would that mean?  Better would be to declare them both as unsigned so that all possible values have an interpretable meaning.

Prefer pass-by-value for simple data

Generally, unless there is a need to modify the underlying variable, it's better to pass a value rather than a pointer.  For instance, this function:

void displayWrongFrequencyMessage(int *frequency) {


Could instead be written like this:

void displayWrongFrequencyMessage(int frequency) {


That way there's no way that the function could alter the original frequency variable and it's likely to be efficiently passed in a register.  

Write code for humans to read it

The runElectrocardiography function is not bad, but it's not easy to understand.  I'd recommend doing something like this instead:

void runElectrocardiography() {
    unsigned dataCount = 0;  // number of data points so far
    unsigned crossingCount = 0; // number of zero-crossings 
    float curr; // the most recent EKG voltage value 
    float prev = 0; // the previous EKG voltage value 

    for (curr = getNewValue(); curr != END_VALUE; curr = getNewValue()) {
        if (isWithinValidRange(curr)) {
            ++dataCount;
            if (isZeroCrossing(prev, curr)) {
                ++crossingCount;
            }
            if (!isFrequencyValid(dataCount, crossingCount)) {
                displayWrongFrequencyMessage(crossingCount);
                dataCount = 0;
                crossingCount = 0;
            }
            prev = curr;
        }
    }
}


Note that the ekgDataModel structure is no longer needed or used.