Looking for AVR C functions

Are you confused? Topic says you are looking for AVR C functions, but then ask for an ARM reference?

Maybe, try this?

I didn't say I was offended, I said I took exception to the term. To me, a language "extension" is a feature or construct supported by the compiler that goes above and beyond that which is required by the language's official standard. What you are referring to is pre-written code (provided in the specific board's Arduino core) that saves the user from reinventing the wheel. Don't get me wrong, that is a highly valuable resource and it would be painful at best to get stuff done without it. But, it's not an "extension" to the C++ language.

I agree that there's no reason you can't do it: I did it myself before I learned C++. However, I can think of many reasons why you shouldn't.

Using a hand-crafted vtable. I've seen it when reviewing code from a very good developer. IIRC, either the IDE we were using for that processor didn't have a C++ compiler, or he was so deep into it that he didn't want to start over with a different language at that point.

To me, if it extends functionality, its an "extension".

You prefer canned subroutine library? Or, gawd forbid, .DLL hell?

No point in being needlessly pedantic unless my context was unclear.

I got recently the C language book from Kernighan, Ritchie and it explains basically everything there. The only issue is that there aren't many microcontroller commands as I found out. So the question is where to find everything C related for microcontrollers?

In fact, there are no "microcontroller commands" in standard C.
For some microcontrollers (notably older micros) there may be specialized hardware that is not acessible, or inconvenient to access, from C, and C compilers for those micros may implement non-standard extensions to C to help programmers. For example, most PIC and 8051 compilers seem to support a mechanism for binding a variable name to a single bit of a peripheral register. Neither avr-gcc nor gcc for arm have such extensions. Such extensions are NOT standardized, so you have to read the particular compiler vendor's documentation. (gcc does have SOME extensions for each of its architectures. You can read the half-page of AVR extensions (mostly having to do with accessing flash) here: avr-gcc - GCC Wiki )

So... A C program for a microcontroller will can usually be divided into parts:

  1. Startup code.
  2. Your C Application Code.
  3. "Standard C" Libraries.
  4. Machine specific definitions.
  5. "Hardware Abstraction" libraries.
  6. ( maybe Operating System and Operating System libraries. )

I think I'll discuss them in more detail in subsequent messages...



The next step for me is moving to STM32 and Cube ide. As I saw there are a lot of similarities and more capabilities. Is there an ARM related C language reference?

There is CMSIS, which is from ARM defines standardized names for what should be common (across all ARM Core-M chips) CPU-specific functions (so that one vendor doesn't call it "SysTick" and another call it "system_timer"), and a layout/format/infrastructure for defining vendor peripheral data structures. In theory, there are also CMSIS definitions for some higher level functions, but they seem to be less accepted (due to wide variations in peripheral behaviors.)

AFAIK, "CubeIDE" is usually considered dependent on ST's HAL functions, which have a questionable reputation.

Let's talk about your application code to start with. That's all going to be standard C code. However, there are a few microcontroller-specific parts.

  1. Your code may spend a lot of it's time and lines of code accessing internal microcontroller peripherals.
    While a lot of C code may look just like a desktop C program, with no hardware dependencies at all, many microcontroller C programs may spend a lot of their time actually fiddling with the peripherals that are built into the chip - some embedded applications are ALL about setting up the peripherals, and the main "program" does nothing. On most modern micros, peripherals are mapped in memory, and accessed via pointers (or are treated as if that is the case.) That may requires slightly more familiarity with using pointers than some C applications, and thinking about them slightly differently. Usually, there will be some vendor-provided definitions that hide some of the complexity and device-dependence. You can say PORTB = 8; or perhaps PORT->GROUP[1].OUTSET.reg = 8 instead of something like *((volatile uint32_t *)0x61004808) = 8; (See later about "machine specific definitions") But you might need to be aware of pointer usage to pass PORTS to functions, and similar.
    Interrupt functions may need special syntax. And you'll need to be aware of concurrency issues sooner than you might have on a desktop. But that's basically it. It's standard C code. x |= (1<<4); sets bit 4 in x, whether x is a memory variable, or a bit in a peripheral function register.
  2. The code needs to be compiled so that it understands where program and data memory exist on the target microcontroller.
    On a desktop, the location of code and data is relatively arbitrary. Somewhere in your binary it will say "my code is at location xxx and my data is at yyy", and the operating system will take care of moving thing around and/or setting up "memory management" to make that happen. On a microcontroller, the memories are usually at fixed locations - program memory is a 0, and MUST have certain things at location 0, data memory is at 0x20000000 (for ARM Cortex - 0x100 for AVR), etc. If you're using gcc, this is usually done by the linker via a thing called the "linker script." This is usually provided by the chip vendor and you don't have to worry about it. Except... on ARM a program loaded by a bootloader may need a different start address than one that is designed to be loaded without a bootloader. And you might have to worry about understanding different "sections" of your program. All chip-family and chip-specific :frowning: (the AVR "PROGMEM" keyword makes use of a separate section, and there are frequently such "helper keywords" defined for common uses. But you'll need to read the specifically-applicable documentation if you need it...)

That brings us to the "Startup code."
C code expects a certain environment - the data in memory, a stack set up, AVR expects register 1 to contain 0, have to figure out where in the code main() is - stuff that can't always be done in C itself (because C is not yet set up.) To handle this, the compiler will usually include code that was written in assembly language for the particular processor, and does all of those "normal C startup things. For CPUs that have an interrupt vector table, that's also usually included in the startup file.
So the AVR C startup file has:

  1. code start vector - a jump from where the processor starts at reset to the start of the startup code. (technically, part of the vector table.)
  2. Interrupt vectors for all the possible interrupts on the chip, containing jumps to a weakly define "bad interrupt" routine.
  3. Zero r1.
  4. Initialize the stack pointer.
  5. Copy all initialized but mutable data from flash to RAM.
  6. Zero all uninitialized variables.
    ARM is similar. C++ adds additional startup code to call constructors (for example.)

ARM makes a big deal out of the fact that you can theoretically write the startup code in C, rather than needing assembly language. But ... IMO, the C you end up using is pretty obscure stuff, so it doesn't really end up any more readable than assembly language, and many vendors still provide the startup code in assembly.

This is probably the piece that OP was most interested in finding, and I'm sorry to say that the news is not good.

C is one of the few languages where you can write a useful program without needing any "functions" or run-time libraries provided by the compiler. This is one of the reasons that C is so popular for microcontrollers - very little space is taken by code that you don't need (and less effort is needed to write a compiler.) However, in the relatively early days of C's popularity, it became apparent that some standardization of library functions would be desirable (you can find early C program filled with conditional compilation that they can compile on AT&T Unix, DEC Unix, HP Unix, SYSV Unix, SUN Unix, or MSDOS. Shudder.)

So a C Standard Library was specified, and most development environments will provide most of the standard C library. Sometimes the library will be highly optimized for the particular microcontroller (like avr-libc for AVRs.) Other times you'll get a fairly generic library written for portability (most ARM environments use "newlib" or "newlib-nano")

These libraries give you string functions, IO functions for file-like things (printf, putc, fopen, etc), math functions (beyond those provided by the compiler), and a bunch of standard ways of getting information about the CPU (INT_MAX, for example, and exact-sized variable types like uint8_t)

However, there are no standard libraries for dealing with microcontroller stuff like IO pins. Arduino has digitalWrite(), STMCube has both HAL_GPIO_WritePin() or LL_GPIO_SetOutputPin(), Atmel ASF has port_pin_set_output_level() (SAMD21) but also pio_set_pin_group_high() (SAM3x: due.) MBedOS seems to prefer defining a C++ object for each pin, and doing ledPin.write(val) or ledPin=1; RPi Pico SDK uses gpio_put() or gpio_put_masked()
Even delay functions have no standard: delay(), _delay_ms(), sleep()...
And it varies by version, too. STM32Cube version 1.2 didn't have LL_GPIO functions. 1.11 does...
It's a mess. You have to look up the documentation for whatever environment you are using (or ignore the libraries and do it yourself via "bare metal" programming.)
If you're doing simple things, the Arduino functions are probably as good as any. For more complex or chip-specific features, you'll need something else.


Which brings us to:

  1. "Hardware Abstraction" libraries.

This is a pretty good overview that shouldn't be "buried" in a couple of posts. Is there a place on the forum where it can be saved or linked to?

As microcontroller datasheets exceed 1000 pages, peripherals become more complex, and (?) perhaps fewer and fewer engineers are "proficient" in both hardware and software "current practices", many chip vendors have been offering assistance in the form of libraries, code generators, and GUI configuration tools. This is supposed to make it easier to use the chips.

There are a couple of problems with such tools.

The most serious problem, in my opinion, is that there is typically no standardization of such libraries. You might think that there might be something as generic as Arduino's "digitalWrite()" for setting/clearing pins; but no, you're likely to have a different function name, and a different way of defining and configuring pins, depending on whether you're using Microchip's ASF, STM's "Cube", ESP's "SDK", or TI's "TivaWare." Worse (?), even with a single vendor, the libraries can be different between different processor families, or even different versions of the tool package. So using these libraries doesn't give you the sort of code portability that you might have been hoping for.

Second, the vendor tools will probably force upon you a particular development environment - things like which IDE you use, the directory structure of your project, and so on. Microchip's ASF (current version is ASF 4, aka "Start") pretty much requires the use of Microchip Studio. Raspberry Pi's SDK for the RP2040 pretty much requires the use of a rather deep directory structure and "cmake." And so on.

Third, vendors seem very reluctant to release such a tool set that doesn't support ALL of the features of their chips. (unlike the Arduino IDE, which gives you a very simple and basic "abstraction" of things like SPI, UART, GPIO, or TWI.) In theory this is a good thing, I guess. In practice, it can mean that using the tools can be nearly as complicated as writing bare metal code straight from the chip datasheet, and the resulting code can be a lot larger than you might expect ("bloated" is the usual term.)

Fourth, the tools sometimes look like the vendor has not really invested in having them be GOOD examples of the relevant code. (A frequent complaint is along the lines of "it looks like they hired a college student as a summer intern (full of the latest buzzwords but without much actual experience), and then threw it out to the users.") This is a contribution to the "bloat" problem, as well as annoying depth of abstractions that makes the code less than readable. The code can have bugs, and because of the way it was written, and because of the "you don't have to read the datasheet" mentality, these can be especially frustrating to try to track down.
I would guess that a lot of the tools, now on their Nth "generation", have improved significantly from older versions. Sometimes that means even more complexity, or needing to use a GUI to set things up. (the GUI isn't always a bad thing - it can help a lot allocating pins which might have any of a dozen or more different functions, for example.)

The general consensus on most of the fora I read is that the vendor libraries are "not good", but sometimes they're "not good" in ways that you don't care about. Also, it can be useful to have a (supposedly) working example for how to use a peripheral. And for more complex peripherals (USB, networking)... there's a lot that you really don't want to have to figure out on your own. (for many wireless networking things (like ESP chips), the wireless stack will be proprietary, so you're pretty much forced to use the vendor libraries.)

(The "bloat" factor starts to be a concern if you want to use smaller chips. It's of little import if the clock initialization code for a SAMD21 occupies about 1K of code, on a part with 256k of flash. (Clock initialization is particularly complex on many 32bit chips, and it's usually something that an application programmer doesn't want to deal with. When I used ASF v3 for SAMD21, the clock code it produced was about 1k.)
But if you're using something like a SAMD11 with only 16k of program space, you begin to get concerned.)

Being familiar with a particular vendor toolset might look good on your resume.

It might be that Arduino is one of the most successful vendor-independent libraries that exists. It's big disadvantage is that it does NOT support the fancy vendor-specific features that might exist.