2
\$\begingroup\$

I'm learning about microcontrollers and have very limited knowledge on the subject.

I just know that one needs to set bits in the registers and manipulate them basically to make a uC work. The registers setup and control the electronics in the micro-controller.

Lets say I wrote a C code in Atmel Studio for a particular 8-bit uC. This uC has register names in its data-sheet like TCNT0 for instance. What if I want to use the same code with another 8-bit AVR? Should I write everything from scratch or just need to make little modifications? Are most of the register names in common?

For example, if I program an ATmega328P with C, would the same code work in any other 8-bit type for example with ATmega168? And if no, would you normally write it from scratch or just modify the previous one?

I'm confused about the code compatibility. How about same question for using 8-bit uC's code for a 32-bit uC like AVR32?

\$\endgroup\$
  • \$\begingroup\$ the problem is some percentage of the code and/or libraries is bare metal talking to specific peripherals and addresses in the chip. No reason to assume from one chip in the same family to completely different chip families or companies to have the same number of peripherals that are identical in design and have the same address. It is possible to create an environment to attempt to increase portability (arduino, mbed) but you still cant ask for things that the chip you are on doesnt have or if there isnt a library for that chip then no. \$\endgroup\$ – old_timer Feb 27 '17 at 22:00
  • \$\begingroup\$ when specific registers are called out it might not even port across the whole family. You have to inspect the two datasheets. \$\endgroup\$ – old_timer Feb 27 '17 at 22:01
3
\$\begingroup\$

For example, if I program an ATmega328P with C, would the same code work in any other 8-bit type for example with ATmega168?

In that case, the answer happens to be yes. The only difference between the ATmega168P and 328P is the amount of flash memory -- so long as your program fits on the 168P (i.e, as long as it isn't larger than 16 KB), it'll run identically on that part.

In other cases… it depends. There are some subtle differences in peripherals and features across the AVR line, even in situations where peripherals have the same name. Compare the datasheets carefully for details.

How about same question for using 8-bit uC's code for a 32-bit uC like AVR32?

AVR32 is a completely different architecture from 8-bit AVR; they have almost nothing in common besides the name. So you'd probably have to rewrite your program.

\$\endgroup\$
  • \$\begingroup\$ At the C level, it's not as different as you make it out to be, but then at the C level, the code for a full-blown Unix program is not all that different. What is significantly different between AVR architectures is the instruction set, memory map and peripherals - so basically everything. But C is still C regardless. \$\endgroup\$ – nsayer Feb 27 '17 at 5:29
3
\$\begingroup\$

Code can be written to be portable between microcontrollers. It is a good idea to do this unless your application is either very simple or you're in a big hurry :)

The idea is to separate the higher-level procedures (your main program flow) from the low-level hardware manipulation (turning on pins, configuring registers, etc).

For a simple example, take a circuit where you want to control an LED. You might set up your code by having a whole separate module for the LEDs: (I don't work with AVRs; please forgive any errors)

led.c:

// For simplicity, this assumes all LEDs are on Port D.

void ledInit(uint8_t ledPin)
{

    DDRD |= (1 << ledPin);   // Configure the pin as an output
    PORTD &= ~(1 << ledPin); // Set the pin low
}

void ledOn(uint8_t ledPin)
{
    PORTD |= (1 << ledPin);
}

void ledOff(uint8_t ledPin)
{
    PORTD &= ~(1 << ledPin);
}

Then, in your mainline code, you would only access the LED via these commands. If you needed more access, like a toggle command, you would add it to the led.c file. The main code isn't even aware of what happens inside the LED module.

This makes it much easier to move your mainline code between microcontrollers. Basically, you would only need to re-write the led.c file. You wouldn't have to change your mainline code at all.

This logic can also be used for timers, communications ports (SPI, I2C, USART...), etc.


In a large project, I find it best to actually have three layers of code:

  • The mainline code, which orchestrates everything
  • Mid-level, which provides functions and names (#defines) to the mainline code. This translates between the intention of the mainline code and the low-level functions required to make things happen, and
  • Low-level (driver), which actually flips the correct bits.

In this case, the low-level functions are only seen by the mid-level block. The mid-level block is exposed to the mainline code.

This may seem overly complex, but it doesn't really take much more work. The benefit happens if you have to change bigger implementation details.

For example, say you've designed your program to use an SPI peripheral, but your new design actual needs to use I2C. You can now change the mid-level code block to properly call the low-level I2C commands instead of the low-level SPI commands. You wouldn't have to change either the high-level or the low-level code.

That being said, I wouldn't recommend this while you're still learning the basics :)

\$\endgroup\$
2
\$\begingroup\$

Short answer, no, C code written for one microcontroller is not guaranteed to run on other microcontrollers.

Long answer, your compiler may take care of such matters to a large extent, and libraries can be written as well which take care of many under-the-hood details.

For example, I tend to use the PIC C compiler from CCS to deal with microcontrollers of the PIC family. Code written for a simple microcontroller will generally run on a complex controller, just by including a different h file. If I try to run peripheral which don't exist, the compiler will give me an error (which prevents one from easily going from a complex controller to a simple one).

There's nothing magic about this. Somebody took the time to write defs for all the relevant memory locations, and write the functions to call these defs and take appropriate actions.

For STM32 core controllers, there are CMSIS libraries, which in theory means that if your chip maker provides a CMSIS library, your code will be FAIRLY portable to other members of the STM32 core family, even those from different makers.

\$\endgroup\$
1
\$\begingroup\$

What if I want to use the same code with another 8-bit AVR?

depends on the nature of the code.

hardware-dependent code obviously will not run on a mcu with different hardware. To the extent the two mcus have the same hardware (TIMER0 for example), the code will run unmodified.

hardware-independent code obviously will run on any mcu, to the extent that the compilers have the support.

\$\endgroup\$
1
\$\begingroup\$

The code might work, that depends on your compiler and if the peripherals used on your code, i.e. timers and counters, have the same registers.

\$\endgroup\$
0
\$\begingroup\$

Register's name use to be quite the same if you are working with the same AVR family. Some register can appear, disappear if the feature are not the same. However, if you go for another uC manufacturer, register's name will be totally different. (If you go from microchip to TI etc...)

You can start from your old code because most uC use C programming. The algorithm part may be unchanged. You will need to adapt register in order to make the uC behave as intended.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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