Is there a standard C for microcontrollers?

Is there any special C standard for microcontrollers?

I ask, because while I was programming something under Windows, it doesn't matter which compiler I used. If I had a compiler for C99 , I knew what I could do with it.

But lately, I started programming in C for microcontrollers, and I was shocked that even he is still C in his fundamentals, for example, in loops, creating variables, and therefore there is some type of syntax that I have never seen in C for desktop computers, And in addition, the syntax changes from version to version. I use the AVR-GCC compiler, and in previous versions you used the function for input-output ports, now you can treat the port as a variable in the new version.

What determines what functions and how to implement them in the compiler, and yet call it C?

+7
c standards embedded microcontroller
source share
7 answers

Embedded systems are weird and sometimes have exceptions for "standard" C.

From system to system, you will have different ways to do things such as declare interrupts, or determine which variables live in different memory segments, or run intrinsics (pseudo-functions that map directly to assembly code) or execute built-in assembly code.

But the basics of the control flow (for / if / while / switch / case) and the declarations of variables and functions should be the same throughout the board.

and in previous versions you used the function for input / output of ports, now you can process a variable of type Port in the new version.

This is not part of the C language; this part of the device support library. What every manufacturer will have to document.

+8
source share

Is there any special C standard for microcontrollers?

No, there is an ISO C standard. Since many small devices have special architecture features that need to be supported, many compilers support language extensions. For example, since the 8051 has an address RAM bit, the _bit data type may be provided. It also has a Harvard architecture , so keywords are provided to indicate different memory address spaces that a single address does not allow, since addressing requires different instructions for these spaces. Such extensions will be clearly indicated in the compiler documentation. Moreover, extensions in the corresponding compiler must have an underscore prefix. However, many of them provide unvarnished aliases for backward compatibility, and their use should be deprecated.

... when I programmed something under Windows, it doesn't matter which compiler I used.

Since the Windows API standard is standardized (Microsoft), and it only works on x86, so there are no architectural changes. However, you can still see the FAR and NEAR macros in the API, and this is a return to the 16-bit x86 with its segmented addressing, which also required processing compiler extensions.

... that even he is still C in his fundamentals, for example, in loops, creating variables, etc.,

I'm not sure what that means. A typical microcontroller application does not have an OS or a simple core, you should expect much more bare metal or a system level because there are no extensive OS interfaces and device driver interfaces to do a lot of work under the hood for you. All of these library calls are just that; they are not part of the language; it is the same C language; jut is put on a different job.

... there is some type of syntax that I have never seen in C for desktops.

For example...?

And besides, the syntax changes from version to version.

I doubt. Again; eg...?

I use the AVR-GCC compiler, and in previous versions you used the function for input-output ports, now you can treat the port as a variable in the new version.

It does not depend on changes in the language or the compiler, but rather, simple "preprocessing magic." In AVR, all I / O operations are mapped to memory, so if, for example, you include the device support header, it may have an announcement, for example:

 #define PORTA (*((volatile char*)0x0100)) 

Then you can write:

 PORTA = 0xFF; 

To write 0xFF to memory, a register is displayed at address 0x100. You can just take a look at the header file and see how it does it.

The GCC documentation describes specific concrete options; AVR is specifically addressed here in section 6.36.8, and in 3.17. 3 . If you compare this to other goals supported by GCC, it has very few extensions, perhaps because the architecture and AVR suite were specifically designed for a clean and efficient implementation of the C compiler without extensions.

What determines what functions and how to implement them in the compiler, and yet call it C?

It is important to understand that the C programming language is a separate object from its libraries, and the functions provided by the libraries are no different from those that you can write yourself - they are not part of the language - so it can be C without any library . Ultimately, library functions are written using the same basic language elements. You cannot expect that the level of abstraction is present, say, in the Win32 API, which exists in the library intended for the microcontroller. In most cases, you can expect at least a subset of the C standard library , as it was designed as a system-level library with a small dependency target equipment.

I have written C and C ++ for embedded and desktop systems for many years and do not recognize the huge differences that you seem to perceive, so you can only assume that they are the result of a misunderstanding of what the C language is. The following books may help.

+17
source share

The C language implies von Neumann (one address space for all code and data), which is actually not all architectures, but most desktop / server machines have (or at least are present using the OS). To get around this without creating horrible programs, the C compiler (using the linker) often supports some extensions that help make efficient use of multiple address spaces. All this may be hidden from the programmer, but it often slows down and inflates programs and data.

As for how you access device registers - on different machines of the class of desktop computers and servers, this is also very different, but since programs written to work under the usual modern operating systems for these computers (Mac OS X, Windows, BSD or Linux ) usually do not access the hardware directly, this is not a problem. However, there is an OS code that should solve these problems. This is usually done by defining macros and / or functions that are implemented differently on different architectures or even have several versions in the same system so that the driver can work for a specific device (such as an Ethernet chip), regardless of whether it is on a PCI or USB key (possibly connected to a USB card inserted in the PCI slot) or directly displayed in the address space of the processor.

In addition, the standard C library makes more assumptions than the compiler (and the language itself) about the system that hosts the programs that use it ( standard C library ). These things just don't make sense when there is no general-purpose OS or file system. fopen makes no sense in a system without a file system, and even printf can be hard to define.

As for the AVR-GCC and its libraries - there are many things that tell how this is done. AVR is a Harvard architecture with memory registers with memory mappings, special function registers and general purpose registers (memory addresses 0-31) and another address space for code and persistent data. This is already beyond what C standard implies. Some of the registers (general, special, and device control) are available with special instructions for things such as switching individual bits and reading / writing to multiple multibyte registers (multi-instruction operation) implicitly blocks interrupts for the next command (so the second half of the operation may happen). This is something that programs on the C desktop should not know anything, and since the AVR-GCC comes from regular GCC , it’s not that they understand all these things initially. This meant that the compiler did not always use the best instructions for accessing control registers, therefore:

 *(DEVICE_REG_ADDR) |= 1; // Set BIT0 of control register REG 

turned into:

 temp_reg = *DEVICE_REG_ADDR; temp_reg |= 1; *DEVICE_REG_ADDR = temp_reg; 

since the AVR usually needs to have things in its general purpose registers to perform bit operations on them, although this is not true for some memory locations. AVR-GCC had to be changed to understand that when the address of a variable used in certain operations is known at compile time and is in a certain range, it can use different instructions to prefix these operations. Prior to this, the AVR-GCC simply provided you with some macros (similar to functions) for which there was a built-in assembly (and use the separate instructions that GCC uses now). If they no longer provide macros for these operations, then this is probably a bad choice, since it breaks the old code, but allows you to access these registers as if they were ordinary variables as soon as the ability to do this efficiently and atomically.

+6
source share

I have never seen a C compiler for a microcontroller that did not have some controller-specific extensions. Some compilers are much closer to ANSI standards than others, but for many microcontrollers, there are trade-offs between performance and ANSI compliance.

On many 8-bit microcontrollers and even on 16-bit systems, access to variables in the stack frame is slow. Some compilers will always allocate automatic variables at runtime, despite the additional code necessary for this, some will allocate automatic variables at compile time (allowing variables that never live at the same time to overlap), and some allow you to control behavior with command parameters strings or directives #pragma . When encoding such machines, I sometimes like the #define macro called "auto", which is redefined to "static" if it helps speed things up.

Some compilers have many storage classes for memory. You can significantly improve performance by saying things are appropriate for storage classes. For example, an 8051 system can have 96 bytes of data memory, 224 bytes of idata memory, which spans the first 96 bytes, and 4K of xdata memory.

  • Variables in the "data" can be accessed directly.

  • Variables in the idata memory can only be accessed by loading their address into a single-byte register pointer. No additional overhead is required in those cases when it is necessary in any case, so idata memory is great for arrays. If the q array is stored in idata memory, the reference to q[i] will be as fast as if it were in the data memory, although the reference to q[0] will be slower (in the data memory, calculate the address and access it without pointer case; in idata memory this is not possible).

  • Variables in xdata memory are much slower than other types, but there is much more xdata memory available.

If someone tells the 8051 compiler to put everything in the β€œdata” by default, one of them will β€œrun out of memory” if one variable has more than 96 bytes and the other does not tell the compiler to put something in another place. If by default everything is placed in "xdata", you can use much more memory without pressing the limit, but everything will work more slowly. It is best to place frequently used variables that will be directly accessed in the "data", frequently used variables and arrays that are indirectly available in "idata", and rarely used variables and arrays in "xdata".

+5
source share

The vast majority of the standard C language is common with microcontrollers. Interrupts tend to have slightly different agreements, although not always.

The processing of ports, such as variables, is the result of the registers being mapped to memory locations on most microcontrollers, therefore, writing to the appropriate memory location (defined as a variable with a predefined memory location), you set the value on this port.

+2
source share

As previous participants have noted, the standard does not exist, mainly due to different architectures.

Having said that, Dynamic C (sold by Rabbit Semiconductor ) is described as "C with real-time extensions." As far as I know, the compiler is aimed only at Rabbit processors, but there are useful additional keywords (for example, costate, cofunc and waitfor), some real features (for example, #use mylib.lib instead of #include mylib.h ), and no linker) and a few omissions from ANSI C (for example, not static variables with a file scope).

It is still described as "C".

+1
source share

Wiring has the C syntax. You might want to see what it does as such.

+1
source share

All Articles