What is SAE J1939?

What is SAE J1939?

The Society of Automotive Engineers (SAE) developed the J1939 standard to be the preferred CAN for equipment used in industries ranging from agriculture, construction, and fire/rescue to forestry, materials handling as well as on and off-highway vehicles. It is a high-level protocol that defines how communication between nodes (modules) occurs on the bus. The J1939 network is a specific communication system, supporting specific sets of applications and a specific industry, rather than being generalized.

 What kinds of messages are sent on a J1939 network?

Any electronic control unit (ECU) using J1939 is permitted to transmit a message on the network when the bus is idle. Every message includes a 29-bit identifier, which defines the message priority, what data is contained within the 8-byte data array that follows the identifier, and which ECU sent the message.

What is a PGN?

A Parameter Group Number (PGN) is a part of the 29-bit identifier sent with every message. The PGN is a combination of the Reserved bit (always 0), the data page bit (currently only 0, 1 is reserved for future use), the PDU Format (PF) and PDU Specific (PS). PDU stands for Protocol Data Unit, and can also be read as the message format. The PF and PS are both a byte (8-bits) long.

 The PS is dependent on the value in the PF field. If the PF value is between 0 and 239, the PS field will contain the destination address of the node that will receive the message. If the Global Address (FF16) is used, then all nodes on the bus will receive the message. This type of message, ” one that can be directed to a specific ECU on the bus by sending the message to its address “, is called a PDU1 message.

 If the PF field is between 240 and 255, then the PS field will contain a Group Extension (GE). The GE provides a larger set of values to identify messages that are broadcasted to all nodes on the network. This type of message, ” one that is sent to all ECUs on the bus “, is called a PDU2 message.

 The PGN uniquely identifies the Parameter Group (PG) that is being transmitted in the message. Each PG (a grouping of specific parameters) has a definition that includes the assignment of each parameter within the 8-byte data field (size in bytes, location of LSB), and the transmission rate and priority of the message. The structure of a PGN permits a total of up to 8672 different parameter groups to be defined per page.

 When an ECU receives a message, it uses the PGN in the identifier to recognize the type of data that was sent in the message.

 What is an SPN?

Each parameter used in the J1939 network is described by the standard. A Suspect Parameter Number (SPN) is a number that has been assigned by the SAE committee to a specific parameter. Each SPN has the following detailed information associated with it: data length (in bytes); data type; resolution, offset; range; and a tag (label) for reference. SPNs that share common characteristics will be grouped into a Parameter Group (PG) and will be transmitted to the network using the same PGN.

What happens if my ECU has parameters that have not been assigned an SPN by the SAE Committee?

The J1939 standard has two types of messages (PGNs) that can be used by manufacturers to handle parameters or messages that are not already covered by the standard.

The Proprietary A PGN (00EF0016) is a PDU1 message, and is used where the manufacture wants to direct the message to a specific destination node. How the data field of this message is used is up to each manufacturer.

Alternatively, Proprietary B PGNs (00FF0016 to 00FFFF16) are PDU2 messages allowing the manufacture to define the GE fields, as they desire. The data length and how the data fields of these messages are used are up to each manufacturer. Two manufacturers may use the same GE value, and the receivers of the information would have to differentiate between the two manufactures.

What happens if I have to send more than 8-bytes of data?

The J1939 standard has defined a method of communicating more than 8 bytes of data by sending the data in packets as specified in the Transport Protocol (TP). There are two types of TP, one for broadcasting the data, and the other for sending it to a specific address.

A Broadcast Announce Message (BAM) is received by all ECUs on the network, and they do not have to send any messages back to the originator in order for the BAM session to proceed. In a BAM session the transmitting module simply sends the data packets one after another at a predefined rate.

A specific TP, however, it directed at a particular address. The ECU at that address must respond to the commands sent by the transmitting module in order for the TP session to proceed. To begin a session, the originator sends a Request to Send (RTS) and the responder sends a Clear to Send (CTS). For this reason, this type of TP is called an RTS/CTS session. Either the originator or the responder can abort an RTS/CTS session at any time.

It is not mandatory for all J1939 ECUs to support TP sessions. If, however, the module does support TP, it must be able to, at a minimum, support one BAM and one RTS/CTS session concurrently from the same source address. It can only originate one destination specific connection with a given destination at a time, but an ECU might be able to support simultaneous CTS/RTS sessions with different addresses. Check with the manufacturer whether or not a node can support multiple simultaneous TP sessions (RTS/CTS and/or BAM).

How are diagnostics supported?

A Diagnostic Message (DM) may be sent and/or received by an ECU. If the ECU supports diagnostic messaging, each type of potential fault in the module will have associated with it a Diagnostic Trouble Code (DTC).

A DTC is a combination of four independent fields: the Suspect Parameter Number (SPN) of the channel or feature that can have faults; a Failure Mode Identifier (FMI) of the specific fault; the occurrence count (OC) of the SPN/FMI combination; and the SPN conversion method (CM) which tells the receiving mode how to interpret the SPN. Together, the SPN, FMI, OC and CM form a number that a diagnostic tool can use to understand the failure that is being reported.

When an ECU detects a fault, it will send an Active Diagnostic Trouble Code, DM1, message. The DM1 message send by the ECU will also contain the status lamps of the module. While the fault is still present, the ECU will continue to broadcast the DM1 message to the network every second. When the fault clears, the ECU will send a final DM1 message showing that there are no further faults present. If multiple faults are present simultaneously, the ECU will send all the active faults in a single DM1 by using the Broadcast Announce Message (BAM) in a Transport Protocol session. Refer to a product’s datasheet to see if it will send a DM1, and what type of DTCs it supports.

An ECU on the bus may respond to the data in the DM1, or a diagnostic tool may be connected to the network to show an operator all the active faults on the network. Refer to a product’s datasheet to see if and how it will respond to a DM1.

There are lots of other features for diagnostics, such as retrieval of information from a log by requesting a DM2, Previously Active DTCs, which are supported by the J1939 standard.

What is a NAME, and why is it important?

A J1939 NAME is comprised of the following fields: Arbitrary Address Capable; Industry Group; Vehicle System Instance; Vehicle System; Reserved Bit; Function; Function Instance; ECU Instance; Manufacturer Code; and Identity Number.

Every ECU on a J1939 network has an exclusive combination of the above fields, which results in a unique NAME for each module. The NAME is not only used to identify the module to other nodes on the bus, but it is also used in Network Management. The combination of the NAME field will form a number, and the lower numeric value NAMEs have a higher priority. If more than one node on the network attempt to claim the same address, they will arbitrate for the address, and the module with the higher priority NAME will be able to claim the address.

Advertisements

Embedded Systems Interview Questions

What is the difference between Synchronous and Asynchronous?

Asynchronous Communication (independent transmit & receive clocks)

  • Asynchronous transmission uses start & stop bits which are added at the start & end of data segments.
  • Asynchronous transmission provides 38.4 Kbps speed.
  • Simple interface (limited data rate, typically < 64 kbps)
  • Used for connecting: Printer, Terminal, Modem, home connections to the Internet
  • No clock sent (Tx & Rx have own clocks)
  • Requires start and stop bits which provides byte timing and increases overhead
  • Parity often used to validate correct reception

Ex:UART

Synchronous Communication (synchronised transmit & receive clocks)

  • Synchronous Transmission does not start & stop bits for sending data segments so a faster transmission speed is achieved
  • Synchronous transmission provides 64 or 128 Kbps speed but
  • More complex interface (high data rates supported up to ~ 10 Gbps)
  • Used for: Connections between computer and telephony networks
  • Clock sent with data (more configuration options)

Ex:I2C and SPI 

What is the difference between RS232 and UART?

RS232 is a specification for serial communications between a DCE and DTE (eg, computer and modem); it defines electrical characteristics, the 25-way ‘D’ connector and the various functions of the various signal lines.

A UART is a Univeral Asynchronous Receiver and Transmitter – it is an electronic circuit which handles communication over an asynchronous serial interface – very often an RS232 interface.

A USART is a Univeral Synchronous/Asynchronous Receiver and Transmitter – it’s like a UART, but also supports Synchronous links

USART is hardware – Universal Synchronous Asynchronous Receive & Transmit Engine.
No fixed protocol (7,8 or 9 bit with or without Receiver Addressing) but is normally capable of protocols that incorporate:

Asynchronous – usually RS232, RS422 & RS485 – two wires TX & RX with pre-defined data rate and receiving synchronised to incoming data stream. No pre-defined master or slave and may be full duplex.

Synchronous – two wires, fixed Clock (from single master) and Data – half-duplex – 8 or 9 bit & no slave address

SPI is a communication protocol – normally a three wire, synchronous, single master, multi-slave & duplex system. Wires are Master-Clock, Data In, Data Out & Slave Select(n).

What is the difference between rs232 and rs485 port?

RS232
full duplex
point to point
short distance(1 meter) at 115kbaud
at least 3 wires (more with handshake control)

RS485
half duplex
point to multi-point (master to multiple slaves)
1200 m distance at 115kbaud
2 wire (RS422 uses 4 wires)

Difference between Rs232 and RS485 is Only Distance RS232 is not for long or far distance.. But RS485 can be use for LONGEST distance ..

RS232 is single ended communication where as RS485 is Differential communication.

Max.no of receivers in RS232 is only ONE, whereas in RS485 is 32.

Max.no of transmitters in RS232 is only ONE whereas in RS485 is 32.

IN RS232 there is no pin to change from transmitter mode receiver mode. But, In RS485 have DE pin to set transmitter mode and RI to set receiver mode.

Baud Rate Formula

 

#include<reg51.h>

void main()
{
unsigned char i = 0xAA;

PCON |= 0x80; //Double the baud rate value
SCON = 0x50; //REN enable & 8-bit uart enable
TMOD = 0x02; //Select auto reload mode in timer 1
TL1 = 0xA0; //Lower Byte
TH1 = 0x96; //Upper Byte
TR1=1; //enable timer

while(1)
{
WDT_RST = 0xA1;

if(TF1)
TF1=0;

if(TI)
{
TI=0;
SBUF=i;

}

if(RI)
{
RI=0;
value=SBUF;
SBUF=value;
}
}
}
for this program i choose the baud rate by using the following formula which i came to know by manual.
baudrate=((2^smod)32*Oscillator Frequency)/(24*(256-TH1))

Framing in Data Transmissions

UART Communication is bit oriented communication

  • Byte-oriented framing    Computer data is normally stored as alphanumeric characters that are encoded with a combination of 8 bits (1 byte). This type of framing differentiates one byte from another. It is an older style of framing that was used in the terminal/mainframe environment. Examples of byte-oriented framing include IBM’s BISYNC protocol.

 

  • Bit-oriented framing    This type of framing allows the sender to transmit a long string of bits at one time. IBM’s SDLC (Synchronous Data Link Control) and HDLC (High-level Data Link Control) are examples of bit-oriented protocols. Most LANs use bit-oriented framing. There is usually a maximum frame size. For example, Ethernet has a maximum frame size of 1,526 bytes. The beginning and end of a frame is signaled with a special bit sequence (01111110 for HDLC). If no data is being transmitted, this same sequence is continuously transmitted so the end systems remain synchronized.

 

  • Clock-based framing    In a clock-based system, a series of repetitive pulses are used to maintain a constant bit rate and keep the digital bits aligned in the data stream. SONET (Synchronous Optical Network) is a synchronous system in which all the clocks in the network are synchronized back to a master clock reference. SONET frames are then positioned within the clocked stream.

What is the maximum distance of the I2C bus?

This depends on the load of the bus and the speed you run at. In typical applications, the length is a few meters (9-12ft). The maximum capacitive load has been specified (see also the electrical Spec’s in the I2C FAQ). Another thing to be taken into account is the amount of noise picked up by long cabling. This noise can disturb the signal transmitted over the bus so badly that it becomes unreadable.

The length can be increased significantly by running at a lower clock frequency. One particular application – clocked at about 500Hz – had a bus length of about 100m (300ft). If you are careful in routing your PCB’s and use proper cabling (twisted pair and/or shielded cable), you can also gain some length.

If you need to go far at high speed, you can use an active current source instead of a simple pull-up resistor. Philips has a standalone product for this purpose. Using a charge pump also reduces “ghost signals” caused by reflections at the end of the bus lines.

I’d like to extend the I2C bus. Is there something like a repeater for I2C?

Yes indeed this exists. Philips manufactures a special chip to buffer the bi-directional lines of the I2C bus. Typically, this is a current amplifier. It  forces current into the wiring (a couple of mA). That way you can overcome the capacitance of long wiring.

However, you will need this component on both sides of the line. The charge pump in this devices can deliver currents up to 30mA which is way too much for a normal I2C chip to handle. With these buffers you can handle loads up to 2nF. The charge amplifier ‘transforms’ this load down to a 200pF load which is still acceptable by I2C components.

Difference between spi and i2c?

SPI or CSI is a simple master/slave communication protocol.

I2C is a networking communication protocol with a multi-master support

Incase of I2C programmer has to take care of right sequence of data transfer on bus like address, start, stop, ack, nak etc…
where as in case of SPI just SS (slave select) is to be managed and data read/write is done from SSPDAT register

1) I2C is half duplex while SPI can be full duplex.
2) Device addressing in I2C is simple because it uses Addresses as part of the data stream. SPI generally requires additional Chip Select signals.
3) SPI can achieve much higher data rates compared to I2C.

context switch

A context switch is the computing process of storing and restoring the state (context) of a CPU so that execution can be resumed from the same point at a later time. This enables multiple processes to share a single CPU resource. The context switch is an essential feature of a multitasking operating system. Context switches are usually computationally intensive and much of the design of operating systems is to optimize the use of context switches. A context switch can mean a register context switch, a task context switch, a thread context switch, or a process context switch. What constitutes the context is determined by the processor and the operating system. Switching from one process to another requires a certain amount of time for doing the administration – saving and loading registers and memory maps, updating various tables and list etc.

Interrupts

An interrupt is an event in hardware that triggers the processor to jump from its current program counter to a specific point in the code. Interrupts are designed to be special events whose occurrence cannot be predicted precisely (or at all). The MSP has many different kinds of events that can trigger interrupts, and for each one the processor will send the execution to a unique, specific point in memory. Each interrupt is assigned a word long segment at the upper end of memory. This is enough memory for a jump to the location in memory where the interrupt will actually be handled. Interrupts in general can be divided into two kinds- maskable and non-maskable. A maskable interrupt is an interrupt whose trigger event is not always important, so the programmer can decide that the event should not cause the program to jump. A non-maskable interrupt (like the reset button) is so important that it should never be ignored. The processor will always jump to this interrupt when it happens. Often, maskable interrupts are turned off by default to simplify the default behavior of the device. Special control registers allow non-maskable and specific non-maskable interrupts to be turned on. Interrupts generally have a “priority;” when two interrupts happen at the same time, the higher priority interrupt will take precedence over the lower priority one. Thus if a peripheral timer goes off at the same time as the reset button is pushed, the processor will ignore the peripheral timer because the reset is more important (higher priority).

interrupt service routine (ISR)

An interrupt service routine (ISR) is a software routine that hardware invokes in response to an interrupt. ISRs examine an interrupt and determine how to handle it. ISRs handle the interrupt, and then return a logical interrupt value. If no further handling is required because the device is disabled or data is buffered, the ISR notifies the kernel with a SYSINTR_NOP return value. An ISR must perform very fast to avoid slowing down the operation of the device and the operation of all lower priority ISRs.

Variables and Data Storage

Where in memory are my variables stored?

Variables can be stored in several places in memory, depending on their lifetime. Variables that are defined outside any function (whether of global or file static scope), and variables that are defined inside a function as static variables, exist for the lifetime of the program’s execution. These variables are stored in the “data segment.” The data segment is a fixed-size area in memory set aside for these variables. The data segment is subdivided into two parts, one for initialized variables and another for uninitialized variables.

Variables that are defined inside a function as auto variables (that are not defined with the keyword static) come into existence when the program begins executing the block of code (delimited by curly braces {}) containing them, and they cease to exist when the program leaves that block of code. Variables that are the arguments to functions exist only during the call to that function. These variables are stored on the “stack.” The stack is an area of memory that starts out small and grows automatically up to some predefined limit. In DOS and other systems without virtual memory, the limit is set either when the program is compiled or when it begins executing. In UNIX and other systems with virtual memory, the limit is set by the system, and it is usually so large that it can be ignored by the programmer.

The third and final area doesn’t actually store variables but can be used to store data pointed to by variables. Pointer variables that are assigned to the result of a call to the malloc() function contain the address of a dynamically allocated area of memory. This memory is in an area called the “heap.” The heap is another area that starts out small and grows, but it grows only when the programmer explicitly calls malloc() or other memory allocation functions, such as calloc(). The heap can share a memory segment with either the data segment or the stack, or it can have its own segment. It all depends on the compiler options and operating system. The heap, like the stack, has a limit on how much it can grow, and the same rules apply as to how that limit is determined.

Frequently Asking Questions in C

1.What is a local block?

A local block is any portion of a C program that is enclosed by the left brace ({) and the right brace (}). A C function contains left and right braces, and therefore anything between the two braces is contained in a local block. An if statement or a switch statement can also contain braces, so the portion of code between these two braces would be considered a local block. Additionally, you might want to create your own local blockwithout the aid of a C function or keyword construct. This is perfectly legal. Variables can be declared within local blocks, but they must be declared only at the beginning of a local block. Variables declared in this manner are visible only within the local block. Duplicate variable names declared within a local block take precedence over variables with the same name declared outside the local block. Here is an example of a program that uses local blocks:

#include <stdio.h>
void main(void);
void main()
{

/* Begin local block for function main() */
int test_var = 10;
printf(“Test variable before the if statement: %d\n”, test_var);
if (test_var > 5)
{

/* Begin local block for “if” statement */
int test_var = 5;
printf(“Test variable within the if statement: %d\n”,
test_var);
{

/* Begin independent local block (not tied to
any function or keyword) */
int test_var = 0;
printf(
“Test variable within the independent local block:%d\n”,
test_var);

}

/* End independent local block */

}
/* End local block for “if” statement */
printf(“Test variable after the if statement: %d\n”, test_var);

}
/* End local block for function main() */

This example program produces the following output:
Test variable before the if statement: 10
Test variable within the if statement: 5

Test variable within the independent local block: 0
Test variable after the if statement: 10
Notice that as each test_var was defined, it took precedence over the previously defined test_var. Also notice that when the if statement local block had ended, the program had reentered the scope of the original test_var, and its value was 10.

2.Should variables be stored in local blocks?
The use of local blocks for storing variables is unusual and therefore should be avoided, with only rare exceptions. One of these exceptions would be for debugging purposes, when you might want to declare a local instance of a global variable to test within your function. You also might want to use a local block when you want to make your program more readable in the current context. Sometimes having the variable declared closer to where it is used makes your program more readable. However, well-written programs usually do not have to resort to declaring variables in this manner, and you should avoid using local blocks.

3.When is a switch statement better than multiple if statements?
A switch statement is generally best to use when you have more than two conditional expressions based on
a single variable of numeric type. For instance, rather than the code

if (x == 1)
printf(“x is equal to one.\n”);
else if (x == 2)
printf(“x is equal to two.\n”);
else if (x == 3)
printf(“x is equal to three.\n”);
else
printf(“x is not equal to one, two, or three.\n”);

the following code is easier to read and maintain:

switch (x)
{
case 1: printf(“x is equal to one.\n”);

Additionally, default cases come in handy for logic checking. For instance, if your switch statement handled a fixed number of conditions and you considered any value outside those conditions to be a logic error, you could insert a default case which would flag that condition. Consider the following example:

void move_cursor(int direction)
{

switch (direction)
{

case UP: cursor_up();
break;
case DOWN: cursor_down();
break;
case LEFT: cursor_left();
break;
case RIGHT: cursor_right();
break;
default: printf(“Logic error on line number %ld!!!\n”, __LINE__);
break;

}

}

4.Can the last case of a switch statement skip including the break?

Even though the last case of a switch statement does not require a break statement at the end, you should add break statements to all cases of the switch statement, including the last case. You should do so primarily because your program has a strong chance of being maintained by someone other than you who might add cases but neglect to notice that the last case has no break statement. This oversight would cause what would formerly be the last case statement to “fall through” to the new statements added to the bottom of the switch statement. Putting a break after each case statement would prevent this possible mishap and make your program more “bulletproof.” Besides, most of today’s optimizing compilers will optimize out the last break, so there will be no performance degradation if you add it.

5.What is the difference between goto and longjmp() and setjmp()?

A goto statement implements a local jump of program execution, and the longjmp() and setjmp() functions implement a nonlocal, or far, jump of program execution. Generally, a jump in execution of any kind should be avoided because it is not considered good programming practice to use such statements as goto and longjmp in your program.
A goto statement simply bypasses code in your program and jumps to a predefined position. To use the goto statement, you give it a labeled position to jump to. This predefined position must be within the same function. You cannot implement gotos between functions. Here is an example of a goto statement:

void bad_programmers_function(void)
{

int x;
printf(“Excuse me while I count to 5000…\n”);
x = 1;
while (1)
{

printf(“%d\n”, x);
if (x == 5000)
goto all_done;
else
x = x + 1;

}
all_done:
printf(“Whew! That wasn’t so bad, was it?\n”);

}

This example could have been written much better, avoiding the use of a goto statement. Here is an example
of an improved implementation:

void better_function(void)
{

int x;
printf(“Excuse me while I count to 5000…\n”);
for (x=1; x<=5000; x++)
printf(“%d\n”, x);
printf(“Whew! That wasn’t so bad, was it?\n”);

}

As previously mentioned, the longjmp() and setjmp() functions implement a nonlocal goto. When your program calls setjmp(), the current state of your program is saved in a structure of type jmp_buf. Later, your program can call the longjmp() function to restore the program’s state as it was when you called setjmp(). Unlike the goto statement, the longjmp() and setjmp() functions do not need to be implemented in the same function. However, there is a major drawback to using these functions: your program, when restored to its previously saved state, will lose its references to any dynamically allocated memory between the longjmp() and the setjmp(). This means you will waste memory for every malloc() or calloc() you have implemented between your longjmp() and setjmp(), and your program will be horribly inefficient. It is highly recommended that you avoid using functions such as longjmp() and setjmp() because they, like the goto statement, are quite often an indication of poor programming practice.

Here is an example of the longjmp() and setjmp() functions:

#include <stdio.h>
#include <setjmp.h>
jmp_buf saved_state;
void main(void);
void call_longjmp(void);
void main(void)
{

int ret_code;
printf(“The current state of the program is being saved…\n”);
ret_code = setjmp(saved_state);
if (ret_code == 1)

{

printf(“The longjmp function has been called.\n”);
printf(“The program’s previous state has been restored.\n”);
exit(0);

}

printf(“I am about to call longjmp and\n”);
printf(“return to the previous program state…\n”);
call_longjmp();
}

void call_longjmp(void)
{

longjmp(saved_state, 1);

}

6.What is an lvalue?

An lvalue is an expression to which a value can be assigned. The lvalue expression is located on the left side of an assignment statement, whereas an rvalue  is located on the right side of an assignment statement. Each assignment statement must have an lvalue and an rvalue. The lvalue expression must reference a storable variable in memory. It cannot be a constant. For instance, the following lines show a few
examples of lvalues:

int x;
int* p_int;
x = 1;
*p_int = 5;

The variable x is an integer, which is a storable location in memory. Therefore, the statement x = 1 qualifies x to be an lvalue. Notice the second assignment statement, *p_int = 5. By using the * modifier to reference the area of memory that p_int points to, *p_int is qualified as an lvalue. In contrast, here are a few examples of what would not be considered lvalues:

#define CONST_VAL 10
int x;
/* example 1 */
1 = x;
/* example 2 */
CONST_VAL = 5;

In both statements, the left side of the statement evaluates to a constant value that cannot be changed because constants do not represent storable locations in memory. Therefore, these two assignment statements do not contain lvalues and will be flagged by your compiler as errors.

7.Can an array be an lvalue?

An lvalue was defined as an expression to which a value can be assigned. Is an array an expression to which we can assign a value? The answer to this question is no, because an array is composed of several separate array elements that cannot be treated as a whole for assignment purposes. The following statement is therefore illegal:

int x[5], y[5]; x = y;

You could, however, use a for loop to iterate through each element of the array and assign values individually, such as in this example:

int i;
int x[5];
int y[5];

for (i=0; i<5; i++)
x[i] = y[i]

Additionally, you might want to copy the whole array all at once. You can do so using a library function such as the memcpy() function, which is shown here:
memcpy(x, y, sizeof(y)); It should be noted here that unlike arrays, structures can be treated as lvalues. Thus, you can assign one structure variable to another structure variable of the same type, such as this:

typedef struct t_name
{
char last_name[25];
char first_name[15];
char middle_init[2];
} NAME;

NAME my_name, your_name;

your_name = my_name;

In the preceding example, the entire contents of the my_name structure were copied into the your_name structure. This is essentially the same as the following line:

memcpy(your_name, my_name, sizeof(your_name));

8.What is an rvalue?

 An rvalue can be defined as an expression that can be assigned to an lvalue. The rvalue appears on the right side of an assignment statement.
Unlike an lvalue, an rvalue can be a constant or an expression, as shown here:

int x, y;
x = 1; /* 1 is an rvalue; x is an lvalue */
y = (x + 1); /* (x + 1) is an rvalue; y is an lvalue */
An assignment statement must have both an lvalue and an rvalue. Therefore, the following statement would not compile because it is missing an rvalue:

int x;
x = void_function_call() /* the function void_function_call() returns nothing */

If the function had returned an integer, it would be considered an rvalue because it evaluates into something that the lvalue, x, can store.

9.What is the difference between ++var and var++?
The ++ operator is called the increment operator. When the operator is placed before the variable (++var), the variable is incremented by 1 before it is used in the expression. When the operator is placed after the variable (var++), the expression is evaluated, and then the variable is incremented by 1. The same holds true for the decrement operator (–). When the operator is placed before the variable, you are said to have a prefix operation. When the operator is placed after the variable, you are said to have a postfix operation. For instance, consider the following example of postfix incrementation:

int x, y;
x = 1;
y = (x++ * 5);

In this example, postfix incrementation is used, and x is not incremented until after the evaluation of the expression is done. Therefore, y evaluates to 1 times 5, or 5. After the evaluation, x is incremented to 2. Now look at an example using prefix incrementation:

int x, y;
x = 1;
y = (++x * 5);

This example is the same as the first one, except that this example uses prefix incrementation rather than postfix. Therefore, x is incremented before the expression is evaluated, making it 2. Hence, y evaluates to 2 times 5, or 10.

10.What does the modulus operator do?
The modulus operator (%) gives the remainder of two divided numbers. For instance, consider the following portion of code:

x = 15/7

If x were an integer, the resulting value of x would be 2. However, consider what would happen if you were to apply the modulus operator to the same equation:
x = 15%7
The result of this expression would be the remainder of 15 divided by 7, or 1. This is to say that 15 divided by 7 is 2 with a remainder of 1.
The modulus operator is commonly used to determine whether one number is evenly divisible into another. For instance, if you wanted to print every third letter of the alphabet, you would use the following code:

int x;
for (x=1; x<=26; x++)
if ((x%3) == 0)
printf(“%c”, x+64);

The preceding example would output the string “cfilorux”, which represents every third letter in the alphabet.

C Coding Guidelines

Scope: In C language, all the declarations should go in one or more header (.h) file(s) and implementation can be in one or more .c file(s). The conventions and standards for constructing these files will be discussed in this document.

1. Declaration
1.1. File Header
The declaration file must have a file header explaining the header file.
Examples:
/***********************************************************************
Project:
Module:
File Name:
Description:
Author:
Creation Date:
Modification log
Date Modified by Reason
Traceability
Req. ID:
Des ID:
***********************************************************************/

The declaration of each variable and function must have line comments. The comments should not exceed the 80th column. Declarations should follow the naming convention for variables and functions. (Refer to the naming conventions section).

1.2. Function Declaration
Comments for a function member must explain,

• Functionality
• Return values and the parameters.
• The function parameters should be on separate lines as seen the following example,
void
FunctionName (int param1,
short param2,
long param3);
• The return type of the function should be written on a separate line directly above the
function name.
• The names of formal arguments to the functions are to be specified and are to be the
same both in the function declaration and in the function definition.
• Always specify return type of a function explicitly.

1.3. Variable Declaration

• Each variable is to be declared in a separate declaration statement.
• Every variable that is declared is to be given a value before it is used.

2. Implementation

2.1. File Header

The implementation file must have a file header explaining the header file.
Examples:
/***********************************************************************
Project:
Module:
File Name:
Description:
Author:
Creation Date:
Modification log
Date Modified by Reason
Traceability
Req. ID :
Des ID :
***********************************************************************/
2.2. Function Header
Function header should look like the following, the header should not exceed the 80th column.
/****************************************************************
Function Name:
Purpose:
Parameters: (Describe each argument, if required.)
In:
In /Out:
Out
Return Value: (Explain if there is any specific reason for having this return type)
Created by:
Date of creation:
***********************************************************************/
2.3. Function Definition
The function parameters should be on separate lines as seen the following example
void
FunctionName (int param1,
short param2,
long param3)
{
//code for carrying out the functionality
}
2.4. Initialisation

• Local variables should be initialised if required when declared.
• Initialise control variables like loop indices in the loop head.
• Use inside braces while initialising multidimensional arrays.
• Do not assign multiple variables the same value with one statement, unless they are
related.

3. Commenting Style

3.1. Line comments

• Line comments should start at 61st column and should not exceed 100th column. If
exceeds 100th column or viewable area continue the comment in the second line
starting from 61st column.
Example:
(61st column of your header / implementation file ) //This line is for example
• Write neat, readable and meaningful comments.

3.2. Block comments
If required add block comments for any block of the code in the files.
• The block comments should not exceed the 60th column in your file.
Example:
/*******************************
The following block of code does so
and so functionality.
********************************/

3.3. Comments for Return values

• Write comments if the return value of a function is not used i.e. if a function returns
int and the programmer does not want to use it, the return value has to be type casted.
E.g.: (void) PrintToFile ();

4. Formatting & Layout Style

• Layout should be written for maximum code readability.
• Use #include <filename.h> for library & system headers.
• Use #include “filename.h” for non-system headers which are present in the local
directory.
• Equivalence should not be checked backwards e.g.: if (false == variableA). It may be
safer but it is more difficult to read.
• Don’t name variables with a leading underscore as this is used by systems to indicate
non-standard extensions.
• Macros (if you use them) should be in upper case.
• The tab size for source files should be set to every four spaces.
• Place a space around operators such as + and ||.
• Use parentheses wherever they may remove possible ambiguity in the minds of the
reader.
• Matching braces (‘{‘ ’}’) should line up vertically inline with the parent statement
e.g.:
void func1(const string& name, long numberOfFoobars)
{
if(…)
{
long i;

}
}
• Place single spaces immediately after commas in routine parameter lists. Eg:
Single spaces in for statement for (int i; i< 10; i++)

5.Naming conventions

5.1. Variables

• Declare the variables as following,

􀂾 int nVariableName;
􀂾 bool bVariableName;
􀂾 double dVaraibleName;
􀂾 short shVariableName;
􀂾 int *pIntVariableName;

Similarly, for other data types.
• For the variable names, each word should begin with capital letter.

5.2. Global variables

• Declare the global variables as following,
gGlobalVariableName
• Avoid using global variables. File level globals should be made static.

5.3. Functions

• For the function names, each word should begin with capital letter.
• Give appropriate function names.
• The function parameters should also follow the naming conventions.

5.4. Naming Files:

• The beginning of each file name must start with a common ID of 2 or 3 characters,
possibly derived from module name.
• The file name should reflect the functionality of routines present in the file.
• Data files that get created automatically or used by the program should contain the
package/module name in the file name or extension.

5.5. Additional Recommendations:

  Keep down the length and complexity of function.
• Each function should have a header comment documenting its function.
• Comment the purpose of code, not the implementation.
• Comment special coding techniques.
• Comment each field of a structure.
• Comment each logical group of statement.
• Comment declarations on the same line.
• Comment end of every function.

6. Macros

• Definition of macros must fit on a single line. If it extends more than one
line use ‘\’ character.
• Definition should not include C keywords.
• Parameters used should be parenthesised.
• Do not include expressions (such as i++ or I–) in defining macro.
E.g.: #define SQUARE(x) (x)*(x)
and using y= SQUARE(i++);
• Use #define to give mnemonic names to construct expressions.
E.g.: #define ROWS 20
• Better define array dimensions in #define.
• # can be surrounded by white spaces.
• For easy maintenance names used in #define should be uppercase and must
be unique, within 7 characters.
• Do not use adhoc syntax expressions like
# define IF if (
# define END IF )
• Use #include to include header information, i.e. common to more than one file.
• Do not use path names either relative or absolute on # include lines.
• Double quotes should be used to include header files in the current
directory and < > for others.
• Do not use multiple header files.
• Better to create separate header file for each logical name.
• Only declarations must be included in the header files and not the definitions

7. Data scope and use of types:

7.1. General

• Limit the scope of data as much as possible.
• Use automatic variables within a subroutine
• Try to both allocate and de-allocate dynamic storage on the same logical
level.
• Typedef declaration should not contain a storage class.
Ex typedef static int I; is not allowed

7.2. Types of variables:

• Avoid using machine nature integers. Instead use type def.

E.g. type def short INT 16
• Use type definitions to create names for commonly used types.
• Use type def to make struct declarations more readable.
• Whenever reasonable declare function as returning short or char not int.
• Declare frequently used automatic or argument variables as register
variables.

 

 

Volatile

Volatile is a qualifier that is applied to a variable when it is declared. It tells the compiler that the value of the variable may change at any time—without any action being taken by the code the compiler finds nearby. The implications of this are quite serious. However, before we examine them, let’s take a look at the syntax.

Syntax

 To declare a variable volatile, include the keyword volatile before or after the data type in the variable definition. For instance both of these declarations will declare foo to be a volatile integer:

 volatile int foo;
int volatile foo;

Now, it turns out that pointers to volatile variables are very common. Both of these declarations declare foo to be a pointer to a volatile integer:

volatile int * foo;
int volatile * foo;

Volatile pointers to non-volatile variables are very rare (I think I’ve used them once), but I’d better go ahead and give you the syntax:

int * volatile foo;

And just for completeness, if you really must have a volatile pointer to a volatile variable, then: int volatile * volatile foo;

Finally, if you apply volatile to a struct or union, the entire contents of the struct/union are volatile. If you don’t want this behavior, you can apply the volatile qualifier to the individual members of the struct/union.

Use

A variable should be declared volatile whenever its value could change unexpectedly. In practice, only three types of variables could change:

  • Memory-mapped peripheral registers
  • Global variables modified by an interrupt service routine
  • Global variables within a multithreaded application

Peripheral registers

Embedded systems contain real hardware, usually with sophisticated peripherals. These peripherals contain registers whose values may change asynchronously to the program flow.

As a very simple example, consider an 8-bit status register at address 0x1234. It is required that you poll the status register until it becomes non-zero.
The naïve and incorrect implementation is as follows:

 To force the compiler to do what we want, we modify the declaration to:

UINT1 volatile * ptr =(UINT1 volatile *) 0x1234;

The assembly language now looks like this:

mov ptr, #0x1234
loop mov a, @ptr
bz loop

The desired behavior is achieved. Subtler problems tend to arise with registers that have special properties. For instance, a lot of peripherals contain registers that are cleared simply by reading them. Extra (or fewer) reads than you are intending can cause quite unexpected results in these cases.

UINT1 * ptr = (UINT1 *) 0x1234; // Wait for register to become non-zero.
while (*ptr == 0); // Do something else.

This will almost certainly fail as soon as you turn the optimizer on, since the compiler will generate assembly language that looks something like this:

mov ptr, #0x1234
mov a, @ptr

loop bz loop

The rationale of the optimizer is quite simple: having already read the variable’s value into the accumulator (on the second line), there is no need to reread it, since the value will always be the same. Thus, in the third line, we end up with an infinite loop.

Interrupt service routines

Interrupt service routines often set variables that are tested in main line code. For example, a serial port interrupt may test each received character to see if it is an ETX character (presumably signifying the end of a message). If the character is an ETX, the ISR might set a global flag.An incorrect implementation of this
might be:

if (ETX == rx_char)
{
etx_rcvd = TRUE;
}

}

int etx_rcvd = FALSE;
void main()
{

while (!ext_rcvd)
{
// Wait
}

}
interrupt void rx_isr(void)
{
..

With optimization turned off, this code might work. However, any half decent optimizer will “break” the code. The problem is that the compiler has no idea that etx_rcvd can be changed within an ISR. As far as the compiler is concerned, the expression !ext_rcvd is always true, and, therefore, you can never exit the while loop. Consequently, all the code after the while loop may simply be removed by the optimizer. If you are lucky, your compiler will warn you about this. If you are unlucky (or you haven’t yet learned to take compiler warnings seriously), your code will fail miserably. Naturally, the blame will be placed on a “lousy
optimizer.”

The solution is to declare the variable etx_rcvdto be volatile. Then all of your problems (well, some of them anyway) will disappear.

Multi-threaded applications

Despite the presence of queues, pipes, and other scheduler-aware communications mechanisms in real-time operating systems, it is still fairly common
for two tasks to exchange information via a shared memory location (that is, a global). When you add a pre-emptive scheduler to your code, your compiler still
has no idea what a context switch is or when one might occur. Thus, another task modifying a shared global is conceptually identical to the problem of interrupt service routines discussed previously. So all shared global variables should be declared volatile.
For example:

int cntr;
void task1(void)
{
cntr = 0;
while (cntr == 0)
{
sleep(1);
}

}
void task2(void)
{

cntr++;
sleep(10);

}

This code will likely fail once the compiler’s optimizer is enabled. Declaring cntr to be volatile is the proper way to solve the problem.

Final thoughts

Some compilers allow you to implicitly declare all variables as volatile. Resist this temptation, since it is essentially a substitute for thought. It also leads to potentially less efficient code. Also, resist the temptation to blame the optimizer or turn it off. Modern optimizers are so good that I cannot remember the last time I came across an optimization bug. In contrast, I come across failures to use volatile with depressing frequency. If you are given a piece of flaky code to “fix,” perform a grep for volatile. If grep comes up empty, the examples given here are probably good places to start looking for problems.