0% found this document useful (0 votes)
55 views41 pages

Advanced C Programming: Real Time Programming Like The Pros

The document discusses various techniques for real-time programming including: using typedefs to create logical data types; performing fixed-point math to represent fractional numbers using integers; filtering signals using moving average and IIR filters; oversampling to improve resolution and reliability; and handling overflow protection to prevent unpredictable behavior. It provides examples of implementing these techniques in C code for microcontrollers.

Uploaded by

quocbao77_ld
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
55 views41 pages

Advanced C Programming: Real Time Programming Like The Pros

The document discusses various techniques for real-time programming including: using typedefs to create logical data types; performing fixed-point math to represent fractional numbers using integers; filtering signals using moving average and IIR filters; oversampling to improve resolution and reliability; and handling overflow protection to prevent unpredictable behavior. It provides examples of implementing these techniques in C code for microcontrollers.

Uploaded by

quocbao77_ld
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 41

Advanced C Programming

Real Time Programming Like the Pros

Contents
typedefs

Fixed-Point
Filtering

Math

Oversampling Overflow

Protection Switch Debouncing

Good Coding Practices

Signed (good) vs Unsigned (bad) Math


for physical calculations

Use Braces { Always } Simple Readable Code

Concept of Self Documenting Code Code as if your grandmother is reading it

Never use Recursion


(watch your stack)

*Disclaimer: Not all code in this presentation follows these practices due to space limitations

Typedefs
Using Naturally Named Data Types

Why Typedef?
You

use variable with logical names, why not use data types with logical names? Is an int 8-bits or 16-bits? Whats a long? Better question: why memorize it? Most integer data types are platform dependent!!! typedefs make your code more portable.

How to use typedefs


1) Create a logical data type scheme. For example, a signed 8-bit number could be s8. 2) Create a typedef.h file for each microcontroller platform you use. 3) #include typedef.h in each of your files. 4) Use your new data type names.

typedef.h Example
typedef typedef typedef typedef typedef typedef unsigned char signed char unsigned short signed short unsigned long signed long u8; s8; u16; s16; u32; s32;

In your code: unsigned char variable; Is replaced with: u8 variable;

Fixed-Point Math
Fractional Numbers Using Integer Data Types

Creating Fractions
Fractions are created by using extra bits below your whole numbers. The programmer is responsible for knowing where the decimal place is. Move the decimal place by using the shift operator (<< or >>). Shifting is multiplying by powers of 2. Ex.: x<<5 = x*2^5; x>>5 = x*2^-5

Fixed Point Fraction Example


A/D Sample (10-bit)

Shift left by 6 (i.e. A2D << 6;):

Whole part

Fractional part

Fractional Example, continued


We

know 5/2 = 2.5 If we used pure integers, 5/2 = 2 (i.e. the number is rounded toward negative infinity) Using a fixed-point fractional portion can recover the lost decimal portion.

Fractional Example, continued


0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 A/D Sample (10-bit) =5

Shift left by 6 (i.e. A2D << 6;):


0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 = 5.0

Whole part

Fractional part

Fractional Example, continued


0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0

Whole part

Fractional part

Divide by 2 (i.e. A2D / 2;): 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 Whole part Fractional part

The whole part: 0000000010(binary) = 2(decimal) The fractional part: 100000(binary) = 32 (huh???)

Fractional Example, continued


Divide by 2 (i.e. A2D / 2;): 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 Whole part Fractional part

The fractional part: 100000(binary) = 32

(huh???)

How many different values can the fractional part be?


Answer: we have 6 bits => 2^6 values = 64 values (i.e.) 111111 + 1(binary) = 64(decimal) Therefore: Fractional part is actually 32/64 = 0.5

Fractional Example, conclusion


Divide by 2 (i.e. A2D / 2;): 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 Whole part Fractional part

-By using a fixed-point fractional part, we can have 5/2 = 2.5

-The more bits you use in your fractional part, the more accuracy you will have.
-Accuracy is 2^-(fraction bits). -For example, if we have 6 bits in our fractional part (like the above example), our accuracy is 2^-6 = 0.015625. In other words, every bit is equal to 0.015625

Fractional Example, example


If we look diving and adding multiple values using this method we can see the benefit of fixed point math. This example assumes we are adding two 5/2 operations as shown.
0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 + 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 Adding 2.5 + 2.5

Once our math operations are complete, we right shift our data to regain our original resolution and data position.
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 =5

Without using the fixed point math the result of the addition would have been 4 due to the truncation of the integer division.

Filtering
Smoothing Your Signals

Filter Types

Filters are classified by what they allow to pass through (NOT what they filter out). For example, a low pass filter (abv. LPF) allows low frequencies to pass through it therefore removes high frequencies. The most common filters are: high pass filters, low pass filters, and band pass filters. We will only cover low pass filters.

Low Pass Filters


Low

Pass Filters (LPFs) are used to smooth out the signal. applications:
Removing sensor noise
Removing unwanted signal frequencies Signal averaging

Common

Low Pass Filters, continued


There

are two basic types of filters:

Infinite Impulse Response (IIR) Finite Impulse Response (FIR)


FIR IIR

filters are moving averages

filters act just like electrical resistor-capacitor filters. IIR filters allow the output of the filter to move a fixed fraction of the way toward the input.

Moving Average (FIR) Filter Example


#define WINDOW_SIZE 16 s16 inputArray[WINDOW_SIZE]; u8 windowPtr; s32 filter; s16 temp; s16 oldestValue = inputArray[windowPtr]; filter += input - oldestValue; inputArray[windowPtr] = input; if (++windowPtr >= WINDOW_SIZE) { windowPtr = 0; }

Moving Average Filter Considerations


For

more filtering effect, use more data points in the average. you are adding a lot of numbers, there is a high chance of overflow take precautions

Since

IIR Filter Example (Floating Point)


#define FILTER_CONST 0.8 static float filtOut; static float filtOut_z; float input; // filter code filtOut_z = filtOut; filtOut = input + FILTER_CONST * (filtOut_z input); // optimized filter code (filtOut_z not needed) filtOut = input + FILTER_CONST * (filtOut input);

IIR Filter Example (Fixed Point)


// filter constant will be 0.75. Get this by // (1 2^-2). Remember X * 2^-2 = X >> 2 #define FILT_SHIFT 2 static s16 filtOut; s16 input;

// filter code filtOut += (input - filtOut) >> FILT_SHIFT;

IIR Filter Example (Fixed Point)


Whoa! How did we get from

filtOut = input + FILTER_CONST * (filtOut input);


To

filtOut += (input - filtOut) >> FILT_SHIFT;


Math: filtOut = input + (1 - 2^-2) * (filtOut input) = input + filtOut input 2^-2*filtOut + 2^-2*input = filtOut + 2^-2 * (input filtOut) filtOut += (input filtOut) >> 2

IIR Filter Considerations (Fixed Point)


For

more filtering effect, make the shift factor bigger. precautions for overflow. can get more resolution by using more shift factors. For example, have your filter constant be

Take You

(1 2^-SHIFT1 2^-2SHIFT2)
(youll have to work out the math!)

Oversampling
Gain resolution and make your data more reliable.

Oversampling Basics
Simple

oversampling: sample more data (i.e. faster sample rate) than you need and average the samples. Even if you dont sample faster, averaging (or filtering the data) can be beneficial.

Oversampling Effects
Helps

to smooth the data. Helps to hide a bad or unreliable sample. Increases A/D resolution if noise is present.

Oversampling Effects

Overflow Protection
Making Sure Your Code Is Predictable

What is Overflow? Why is it Bad?


Overflow

is when you try to store a number that is too large for its data type. For example, what happens to the following code?
s8 test = 100; test = test + 50;

Overflow Protection Methods


1.

2.

3.

Create a new temporary variable using a data type with a larger range to do the calculation. Compare the sign of the variable before and after the calculation. Did the sign change when it shouldnt have? (for signed variables) Compare the variable after the calculation to the value before. Did the value decrease when it should have increased?

Overflow Protection, Example 1


s16 add16(s16 adder1, s16 adder2); // prototype S16 add16(s16 adder1, s16 adder2) { s32 temp = (s32)adder1 + (s32)adder2; if (temp > 32767) // overflow will occur return 32767; else if (temp < -32768) // underflow return -32768; else return (s16)temp;

}
*This example uses a s32 (larger) data value for overflow checking

Overflow Protection, Example 2


// prototype s16 addTo16bit(s16 start, s16 adder); S16 addTo16bit(s16 start, s16 adder) { s16 temp = start; start += adder; if ((start > 0) && (adder > 0) && (temp <= 0)) return 32767; // Overflow occurred else if ((start < 0) && (adder < 0) && (temp >= 0)) return -32768; // Underflow occurred else return start;

}
*This example uses 16 bit values only to check for overflow on signed values this provides improved efficiency on 16 bit platforms.

Overflow Protection, Example 3


// prototype u16 addToUnsigned16bit(u16 start, s16 adder); S16 addToUnsigned16bit(u16 start, s16 adder) { u16 temp = start; start += adder; if ((adder > 0) && (start < temp)) return 65536; // Overflow occurred else if ((adder < 0) && (start > temp)) return 0; // underflow occurred else return start;

}
*This example checks for overflow on unsigned values

Switch Debouncing
Having Confidence in Switch Inputs

Why Debounce? When to Use It?


Debouncing

a switch input reduces erroneous inputs. Use debouncing when pressing a switch starts a sequence or changes the state of something.

When to Debounce Examples


Debounce

when a single push of the switch changes a state. Examples: - pneumatic gripper - motorized gripper where a single push causes the motor to go until a limit switch is reached Do not debounce if constant driver input is needed.

What is Debouncing?
Basically, debouncing means to require a certain number of samples before you confirm the switch input. Various debounce schemes can be used: - require N consecutive samples (i.e. reset the counter if one sample fails) - count up / count down (i.e., if one sample fails, decrement the counter by 1 rather than resetting to zero.

Debounce Example
// debounce opening gripper if (!pneumaticGripperOpenState) { if (gripperOpenSwitch == ON) gripperOpenDebCount++; else gripperOpenDebCount = 0; if (gripperOpenDebCount >= DEBOUNCE_LIMIT) { gripperOpenDebCount = 0; pneumaticGripperOpenState = TRUE; } }

You might also like