Best Practices on Structural Programming

Modular Programming: Modular programming involves dividing code into smaller, independent modules. Modules can be reused multiple times and are easy to test. Modular programming increases the readability and understandability of the code. Each module should have a clearly defined function and interface. Modules should be independent of each other, which facilitates their modification and development.

Impact of AVR Architecture Variations on Modular Programming

Although AVR microcontrollers share a common 8‑bit RISC architecture, different models often vary in several important aspects. These differences influence how software should be structured, especially when aiming for portability and long‑term maintainability.

1. Differences in Register Addresses

AVR devices frequently place hardware registers at different memory addresses. Even peripherals with identical functionality—such as timers, UART, SPI, or ADC—may be mapped to different I/O locations depending on the specific AVR model. Examples:

  • ATmega328P and ATmega32 have similar peripherals but located at different I/O addresses.
  • Larger devices such as ATmega128 or ATmega2560 introduce extended I/O space, shifting many registers to higher addresses.

Programming Implications

Hard‑coding register addresses or device‑specific constants inside logic modules makes the code non‑portable.To avoid this:

  • Always use symbolic register names from `<avr/io.h>`.
  • Keep hardware‑dependent definitions in separate configuration files.
  • Avoid embedding numeric addresses directly in application logic. This ensures that the same module can be compiled for different AVR models without modification.

2. Differences in Peripheral Availability

Not all AVR microcontrollers include the same set of peripherals:

  • Some have one USART, others have four.
  • Some include 16‑bit timers, others only 8‑bit.
  • Some have ADC, SPI, TWI, or analog comparators, while tinyAVR devices may lack several of these.

Programming Implications

Modules that depend on specific peripherals must be written with flexibility in mind. Recommended practices include:

  • Using conditional compilation (`#ifdef`, `#if defined(…)`).
  • Creating abstraction layers that hide hardware differences.
  • Designing modules so they can operate even when certain peripherals are unavailable. This approach allows the same codebase to support multiple AVR families.

3. Differences in Interrupt Vector Layout

AVR devices differ in:

  • number of interrupt sources,
  • order of interrupt vectors,
  • naming of interrupt handlers.

Programming Implications

Interrupt‑driven modules must rely on symbolic ISR names rather than fixed vector numbers. Example:Vis mindreKodeblokk er utvidet ISR(TIMER1_COMPA_vect)

This ensures that the correct interrupt is used regardless of the device’s vector table layout.


4. Differences in Memory Size and Addressing

Larger AVR devices (e.g., ATmega128, ATmega2560) require extended addressing mechanisms:

  • RAMPX, RAMPY, RAMPZ, RAMPD for data memory beyond 64 KB
  • EIND for program memory beyond 128 KB
  • ELPM instead of LPM for reading from extended Flash

Programming Implications

Modules accessing program memory or large data structures must:

  • use the correct load instructions (*LPM* vs *ELPM*),
  • handle extended addressing registers,
  • avoid assumptions about pointer size or memory layout.

This highlights the importance of isolating memory‑related operations into dedicated modules.


How Modular Programming Helps Manage AVR Differences

A well‑structured modular design allows hardware‑dependent code to be isolated from application logic. This is typically achieved through the following layering:


1. Hardware Abstraction Layer (HAL) *

HAL provides a unified interface to peripherals, regardless of the underlying AVR model.

Example functions:

  • `uart_init()`
  • `uart_send()`
  • `uart_receive()`

Internally, HAL uses different registers depending on the device, but the application code remains unchanged.


2. Device‑Specific Configuration Modules

These modules contain:

  • clock settings,
  • timer configurations,
  • peripheral initialization.

By placing these in separate files, it becomes easy to adapt the project to a new AVR model.


3. Reusable High‑Level Modules

These modules remain independent of hardware details and include:

  • communication protocols,
  • algorithms,
  • state machines.

Such modules can be reused across many different AVRs.


Summary

Differences between AVR models—register locations, peripheral availability, interrupt structures, memory addressing—directly affect software design. Modular programming provides a robust framework for handling these variations by separating hardware‑specific code from application logic.

This ensures:

  • portability,
  • easier maintenance,
  • reusability across different AVR families.
en/multiasm/piot/chapter_4_8.txt · Last modified: 2026/01/19 12:46 by marcin
CC Attribution-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0