25
\$\begingroup\$

I have many years of experience with 8-bit cores from various manufacturers - namely 8051, PIC, and AVR - and I now have a Cortex M0 to figure out. Specifically this one, but I hope we can be more general than that.

It's turning out to be a bit more than I bargained for, with multiple documents that describe different parts of the system in varying levels of detail and none really that I've seen to connect it all together. This compared to having one datasheet that explains everything. I understand having much more stuff to document in the first place, but the change in format is throwing me for a loop.

The website above has one document that's a good overview of each subsystem and peripheral in isolation, and another one that describes each register in detail, and I have all the source code for their SDK including header files and some complex examples, but I still see nothing that describes how it all connects together.

Is there a concise walkthrough of the Cortex architecture that explains the function of things that smaller controllers just don't have - like multiple layers of busses from CPU to peripherals, each with its own watchdog timer - and how they all connect together?

\$\endgroup\$
4
  • \$\begingroup\$ I can't tell from what you described if you got the actual data sheet/ user manual for the processor from the manufacturer. That should give you a good overall picture and details. From one of the pictures on the site you linked it looks like an NXP processor. Look at the manufacturer's part number and search for the processor's documentation on their site. There's also ARM's site arm.com/products/processors/cortex-m/cortex-m0.php. \$\endgroup\$ Jun 22, 2015 at 23:19
  • \$\begingroup\$ Sorry for the delay in getting back to this; I've been busy with other projects. Thanks @Adam for the voice of experience. \$\endgroup\$
    – AaronD
    Jun 29, 2015 at 14:39
  • \$\begingroup\$ And thanks @Richard for the overview of how to think about it and the note about printing the pin configuration. I upvoted you both. \$\endgroup\$
    – AaronD
    Jun 29, 2015 at 14:39
  • \$\begingroup\$ You'll never go back, except if you find some extreme price requirements. And even then you'll be tempted, since there are quite a few dirt cheap Cortex-M. \$\endgroup\$ Dec 9, 2016 at 17:32

6 Answers 6

41
\$\begingroup\$

I've worked on AVRs as well as ARM Cortex-M3/M4/R4-based MCUs. I think I can offer some general advice. This will assume you're programming in C, not assembly.

The CPU is actually the easy part. The basic C data types will be different sizes, but you're using uint8/16/32_t anyway, right? :-) And now all integer types should be reasonably fast, with 32-bit (int) being the fastest. You probably don't have an FPU, so continue to avoid floats and doubles.

First, work on your understanding of the system-level architecture. This means IOs, clocking, memory, resets, and interrupts. Also, you need to get used to the idea of memory-mapped peripherals. On AVR you can avoid thinking about that because the registers have unique names with unique global variables defined for them. On more complex systems, it's common to refer to registers by a base address and an offset. It all boils down to pointer arithmetic. If you're not comfortable with pointers, start learning now.

For IOs, figure out how the peripheral muxing is handled. Is there a central mux control to select which pins are peripheral signals and which are GPIOs? Or do you set pins to peripheral mode using the peripheral registers? And of course you'll need to know how to configure GPIOs as inputs and outputs, and enable open-drain mode and pull-ups/downs. External interrupts usually fall into this category as well. GPIOs are pretty generic, so your experience should serve you well here.

Clocking boils down to a few things. You start with a clock source, typically a crystal or internal RC oscillator. This is used to create one or more system-level clock domains. Higher-speed chips will use a PLL, which you can think of as a frequency multiplier. There will be also clock dividers at various points. They key things to consider are what your CPU clock frequency should be and what bit rates you need for your communication peripherals. Usually this is pretty flexible. When you get more advanced, you can learn about things like low-power modes, which are usually based on clock gating.

Memory means flash and RAM. If you have enough RAM, it's often faster to keep your program there during early development so you don't have to program the flash over and over. The big issue here is memory management. Your vendor should provide sample linker scripts, but you might need to allocate more memory to code, constants, global variables, or the stack depending on the nature of your program. More advanced topics include code security and run-time flash programming.

Resets are pretty straightforward. Usually you only have to look out for the watchdog timer, which may be enabled by default. Resets are more important during debugging when you run the same code over and over. It's easy to miss a bug due to sequencing issues that way.

There are two things you need to know about interrupts -- how you enable and disable them, and how you configure the interrupt vectors. AVR-GCC does the latter for you with the ISR() macros, but on other architectures you might have to write a function address to a register manually.

Microcontroller peripherals are usually independent of each other, so you can learn them one at a time. It might help to pick one peripheral and use it to learn part of the system-level stuff. Comm peripherals and PWMs are good for clocking and IOs, and timers are good for interrupts.

Don't be intimidated by the level of complexity. Those "basic" microcontrollers have already taught you much of what you need to know. Please let me know if you need me to clarify anything.

\$\endgroup\$
8
  • 6
    \$\begingroup\$ Good answer. Another thing to watch out for is DMA peripherals, which tend to have complicated and under-documented interfaces in my experience. \$\endgroup\$ Jun 23, 2015 at 4:41
  • 3
    \$\begingroup\$ "And now all integer types should be equally fast." Actually, due to integer promotion rules in C, using 8/16-bit types can result in a lot of sign/zero extension, and can be a problem when Flash memory is low. So if there is RAM to spare, it might pay off to use more 32-bit types or at least prefer int/int_leastN_T types for stack variables. \$\endgroup\$
    – user694733
    Jun 23, 2015 at 8:05
  • \$\begingroup\$ Did a mistake on my comment. I meant to say; use int_fastN_t types, not int_leastN_t types. \$\endgroup\$
    – user694733
    Jun 23, 2015 at 14:20
  • \$\begingroup\$ @user694733: I wish the C Standard would allow code to ask for an integer that occupies a given size in memory and can operate on values within a particular range, but with loosely-specified semantics when going beyond that range. On something like the ARM, an int16_t will often be just as fast as int32_t for values stored in memory, but the Standard requires that on platforms where int is 17 bits or larger, int16_t x=32767; x+=2; must set x to -32767, frequently requiring sign-extension instructions even if code would never make use of the wrapping behavior. \$\endgroup\$
    – supercat
    Jun 23, 2015 at 23:44
  • \$\begingroup\$ @supercat C standard requires wrapping behaviour only for unsigned types. For signed types any wrapping is UB, because of different possible representations. So with x+=2, it would be legal to use instruction for 16-bit types, because compiler may assume that value will not wrap, and thus using it wouldn't change observable behaviour. But I think that ARM doesn't have 16-bit ADD instruction which would make this possible. (I could be wrong, my knowledge on ARM instruction set is not that good.) \$\endgroup\$
    – user694733
    Jun 24, 2015 at 6:33
8
\$\begingroup\$

It's useful to remember that ARM owns the intellectual property for the microprocessor, but doesn't actually make parts. Instead, manufacturers license the various ARM processor versions and produce their own unique parts with individual mixes of features and peripherals.

With that said, if you're new to the architecture, it would probably make sense to start with ARM's documentation which is, essentially, the baseline documentation for all such microprocessors.

For example, the Cortex-M0 is described on ARM's website.

There is also a list of ARM-related books which caters to a wide variety of needs and interests.

Finally, there are the specific manufacturer's datasheets. For the M0, Cypress, NXP and STMicroelectronics are just three of the many manufacturers of real parts based on the Cortex-M0.

(And no, I don't work for ARM and never have.)

\$\endgroup\$
2
  • 1
    \$\begingroup\$ This is a pretty generic answer that doesn't do much more than link to some Cortex-M0 docs, which I'm sure the OP can find on his own. \$\endgroup\$ Feb 22, 2016 at 7:17
  • 1
    \$\begingroup\$ It addresses the question directly which asked for help finding overall documentation. This answer directly answers that need and explains why things are as they are. \$\endgroup\$
    – Edward
    Feb 22, 2016 at 13:16
8
\$\begingroup\$

One big difference is the use of vendor-supplied libraries. For the PICs, Atmels, etc, the basic libraries (for gpio, timers, adc, etc) weren't used much by most developers. In my experience, people would (at most) use them as guides when writing their own code.

However, with ARM, the libraries are almost always used. There is a standard, "CMSIS", that manufacturers are recommended to follow. Most do. It aids in code portability (between different ARMs and between manufacturers), and gives a "standardized" method for structuring your code. People get used to seeing and understanding the library functions.

Sure there are some developers that access the registers directly, but they're the outliers :)

To answer your question, I found it very helpful to read through the Library documentation. ST has well-developed code, with a large Doxygen-created help file. You can see what all the options are for each hardware module.

To use GPIO as an example, the initialization function handles:

  • Direction (In or Out)
  • pullups/pulldowns
  • open-collector/push-pull
  • slew rate
  • etc.

By looking at the options, you can see what is possible. And, of course, you'll learn how to pass these options into the Init function!

OK, now that I've said that, I see that your specific ARM doesn't have CMSIS-compliant libraries. Instead, they have their proprietary SDK available for download. I would start looking though their SDK docs.

If you're not married to this specific product, I might recommend that you find a different vendor with more compliant libraries. You're going to climb a learning curve anyway, so you might as well make your investment more portable...

ARMs are fun! I haven't looked back.

\$\endgroup\$
7
  • \$\begingroup\$ " For the PICs, Atmels, etc, the libraries weren't used much by most developers." Not sure where that comes from. I've only used PICs, not AVR, but certainly wouldn't want to write my own library, for example, for the USB host interface, or TCP stack, or SD card file system. Microchip's libraries for all of these seem quite adequate. \$\endgroup\$
    – tcrosley
    Jun 23, 2015 at 2:36
  • \$\begingroup\$ Ah, @tcrosley, you are definately correct. I was only trying to refer to the functionality covered by the basic peripherals: gpio, timers, adc, etc. \$\endgroup\$
    – bitsmack
    Jun 23, 2015 at 3:09
  • \$\begingroup\$ I agree, I usually access GPIO, timers, clocks, and UART R/W directly. I sometimes use their library calls for I2C, SPI, ADC and UART setup but not always. Lots of registers, whether you're talking PIC (and in particular PIC32) or ARM. \$\endgroup\$
    – tcrosley
    Jun 23, 2015 at 3:18
  • \$\begingroup\$ I think this answer is the most pragmatic one listed for this question, even though it doesn't apply to the OP's specific controller. I think you can do quite a bit of good embedded engineering without understanding the AHB or the NVIC. \$\endgroup\$ Feb 22, 2016 at 7:20
  • \$\begingroup\$ @JayCarlson Thanks! Your edit to this answer was rejected because we're not supposed to change other people's posts so significantly. But it was really good info! I suggest that you post it as your own answer, so it will help people and also get upvoted :) \$\endgroup\$
    – bitsmack
    Feb 22, 2016 at 13:29
5
\$\begingroup\$

Good time to be moving; the 8-bits are dying rapidly; when you can buy a $5 board with (for example) an STM32F103 which is a rather capable 32-bit ARM microcontroller (with USB even!), there's no doubt times have changed.

You've had some excellent answers already but primarily I'd say "forget assembly" and almost "forget caring about how the cpu works at a low level" - one day there'll be a corner case where you need to dig into it (a specific optimization or for debugging) but ARM cores run C code exceptionally well (by design) and you vary rarely need to venture deep inside the guts.

This does mean you'll spend a certain amount of time banging your head against issues with compilers (and especially linkers and makefiles) barfing obscure errors at you but they're all surmountable.

The guts of how the ARMs work (i.e. the ARM cpu books) are dense and not very interesting until such day as you actually need to optimize (and you'll be amazed how infrequently that is when you have 32 bit registers and your PLL'd CPU clock is in the region of 100mhz).

The "old skool" ARM instruction set is much easier to read a disassembly of than the much newer "Thumb2" - which is what you find on most modern microcontroller-level ARMs (Cortex) - but again the innards of the assembly-language instructions mostly fade into the background; if you have the right toolset (especially a decent source-level debugger with breakpoints/single step etc) you just don't care too much about it being ARM at all.

Once you're in the world of 32-bit registers and 32-bit data bus widths and everything you ever wanted available on-chip you'll never want to go back to an 8-bit CPU again; basically there's often no penalty for "taking it easy" and writing code to be legible more than efficient.

However... peripherals... aye and THERE'S the rub.

You sure do get a ton of stuff to play with on modern MCUs, and a lot of it is pretty fancy stuff; you often find a world of sophistication far, far beyond AVR, PIC and 8051 on-chip peripherals.

One programmable timer? Nah, have eight! DMA? How about 12 channels with programmable priority and burst mode and chained mode and auto-reload and.. and.. and...

I2C? I2S? Dozens of pin muxing options? Fifteen different ways to reprogram the on-chip flash? Sure!

It often feels like you've gone from famine to feast with the peripherals and it's common that there's whole chunks of a chip you'll admire but barely use (hence; clock gating).

The amount of on-chip hardware (and variations on that in just one vendor's line of chips) is nowadays fairly mind-boggling. One chip vendor will of course tend to re-use IP blocks so once you get familiar with a certain brand it gets easier but "shit done got craaaazy nowadays."

If anything the peripherals and their interactions (and DMA and interrupts and bus allocation and and and...) are SO complex (and, on occasion, not exactly as described in the datasheets) that engineers frequently have a favorite range of ARM MCUs and tend to want to stick with it simply because they're familiar with the peripherals and development tools.

Good libraries and development tools (i.e. fast compile+debug cycle with a proper debugger) and a large set of working example code projects are absolutely crucial to your ARM MCU choice nowadays. It seems most vendors now have exceedingly cheap evaluation boards (

As I'm sure you've noticed, once you get beyond the microcontroller level with ARMs and into the SOC level (e.g. Raspberry Pi/etc style SOCs) then the rules change completely and it's all about which sort of Linux you're going to run, because - with vanishingly few exceptions - you'd be barking mad to attempt anything else.

Basically; regardless of the CPU that (may) have been pre-selected for you on this gig, buy yourself a handful of super-cheap Cortex-based evaluation boards from a few different vendors (TI, STM, Freescale and more come to mind) and have a hack around with the provided sample code.

Final piece of advice; once you find the page-or-three in the datasheet that describes the pin-muxing options for the exact part number chip you're working with, you might want to print it out and stick it on the wall. Finding out late in a project that a certain combination of peripherals is impossible because of pin muxing is no fun, and sometimes that info is so buried away you'd swear they're trying to hide it :-)

\$\endgroup\$
1
  • \$\begingroup\$ a quick addendum - if your project is much more than the most simplistic controller, think about using an RTOS - there's something of a learning curve with whatever you choose, but even the smallest ARMs have plenty of oomph nowadays for running a multithreaded OS. Personally I've found ChibiOS to be a great mix of lean-yet-capable (especially running on STM32 where it comes with a nice peripheral library) but there's a number of choices. \$\endgroup\$ Jun 24, 2015 at 10:26
4
\$\begingroup\$

I've also come from AVR and now usually stick with STM32 (Cortex-M). Here's what I recommend for starters, and reflects my own difficulties when I began:

  1. Get a board with a debugger, or at least a JTAG connector (and then buy a JTAG debugger). There are many cheap ones around, and you will save a lot of time by using it.

  2. Get a good IDE with everything included. I used to recommend the CooCox CoIDE a long time ago. Since then it has stopped and restarted development, so I'm not sure how it is now. "A good IDE" allows you to get the basic Hello World LED blinking in no time.

  3. "A good IDE" should setup the manufacturer's CMSIS headers. This is basically the register maps that allow easier writing of C/C++ programs, with variable names instead of plain numbers and pointers.

  4. Try to use the manufacturer's peripheral libraries, if you don't need the absolute best performance. You actually don't for now, since you're learning. If later you find you need to squeeze more, look into the library code to see how it does what. The good thing about libraries is also that they usually allow you to use many different chips from the same manufacturer with the same code.

  5. Differently from AVR, the ARM chips start with peripherals disabled. You need to enable them first. A good peripheral library will have examples on how to use the peripherals properly, and you can get some more info from the device's datasheet. So, remember to enable the clocks and peripherals before you use them. Yes, even the I/O ports are considered peripherals.

  6. Code as you learn. Don't try to grok everything at once, since it really is quite complex. I'd start by learning the clock tree (APB, AHB, etc buses) and how clocks and clock dividers interact. Then I'd look where the IDE stores the linker scripts and startup code for your device. The linker script is pretty much how you organize the memory (where is RAM, flash, ISR vector table, etc). The startup script sets up your program (things like copying global variable initializers from flash to RAM). Some IDEs have startup scripts in ASM and some have in C. Sometimes you can Google for another one, in the language you prefer.

  7. Get the debugger going ASAP. It's quite common to make a mistake in the beginning, by doing some stuff (usually hardware initialization) in a different order than you should. This sometimes triggers an ISR exception that gets you in while(1); infinite loop (default implementation for that ISR) that halts your program and it's hard to trace even with a debugger. Imagine without a debugger.

  8. Talking about a debugger, try to get the UART going too, then use a serial-USB adapter to read that. printf() debugging is always useful :-)

\$\endgroup\$
3
\$\begingroup\$

I have not work much on 8051, AVR, or PIC. But recently I have started looking at the ARM Cortex MX line of processors. Therefore I cannot tell you much about transition from 8051, AVR or PIC, but mostly from a standpoint of a beginner.

The ARM®Cortex™-M4 processor is base on Harvard architecture, thus has separate data and instruction buses. Below is an high level image.

enter image description here

This week NXP represntatives will be visiting our facility. I will check with them for any NXP ARM-Cortex Mx resources and post them here. Freescale has Kinetis Low Power 32-bit Microcontroller (MCUs) based on ARM® Cortex®-M Cores, I understand they also have similar guides to learning ARM processors. Unfortunately I have not research them.


References:

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.