Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
en:multiasm:exercisesbook:arduinouno [2026/05/04 10:05] pczekalskien:multiasm:exercisesbook:arduinouno [2026/05/04 14:55] (current) – [Function Call Standards] ktokarz
Line 33: Line 33:
 Source code needs to use explicit declarations to tell the GCC-AVR toolkit how to handle the contents: whether it is code or data, whether the variable is read-only, whether it should be stored across updates, and so on. There are two possible approaches to is: one is to use declarations ('.section'), the other is to manually handle addresses.\\ Source code needs to use explicit declarations to tell the GCC-AVR toolkit how to handle the contents: whether it is code or data, whether the variable is read-only, whether it should be stored across updates, and so on. There are two possible approaches to is: one is to use declarations ('.section'), the other is to manually handle addresses.\\
 There are five ''.section'' declarations, as presented in table {{ref>arduinomemorysections}}. There are five ''.section'' declarations, as presented in table {{ref>arduinomemorysections}}.
-Each of them does the job of ''.org <address>'' in a more elegant way: e.g. ''.section .data'' is equivalent to ''.org 0x800100'' - one does not need to remember the addresses.\\ 
  
 <table arduinomemorysections> <table arduinomemorysections>
Line 44: Line 43:
 | **.eeprom** | Long-term storage | EEPROM | No | | **.eeprom** | Long-term storage | EEPROM | No |
 </table> </table>
- +The AVR GCC compiler uses virtual addresses to distinguish the nature of objects; thus, each section (corresponding to a kind of memory) has a separate virtual address that distinguishes sectionsThus, you either use explicit section definitions and then local offsets using the ''.org'' directive for absolute or virtual addresses.\\ It is, because Flash, EEPROM, and SRAM all start at 0x0000 (see figure {{ref>arduinomemorymap}}, and you need to tell the linker (via source code) which memory block you're referring to. Writing ''.org 0x0100'' may be misleading - e.g. the compiler will assume it is located in flash instead of SRAM.\\
-We mentioned before that ''.section .data'' is equivalent to ''.org 0x800100''Why ''.org 0x800100'' instead just ''.org 0x0100''?\\ +
-It is, because Flash, EEPROM, and SRAM all start at 0x0000 (see figure {{ref>arduinomemorymap}}, and you need to tell the linker (via source code) which memory block you're referring to. Writing ''.org 0x0100'' may be misleading - the compiler will assume it is located in flash instead of SRAM.\\+
 For this reason, the way the AVR-GCC toolchain (assembler and linker) handles Harvard Architecture in Arduino Uno (ATMega328P) is the use of virtual memory offsets: ''0x000000'' means it is Flash, ''0x800100'' means it is SRAM (built-in) and ''0x810000'' means it is EEPROM. Details are presented in table {{ref>arduinomemoryblocks}}. For this reason, the way the AVR-GCC toolchain (assembler and linker) handles Harvard Architecture in Arduino Uno (ATMega328P) is the use of virtual memory offsets: ''0x000000'' means it is Flash, ''0x800100'' means it is SRAM (built-in) and ''0x810000'' means it is EEPROM. Details are presented in table {{ref>arduinomemoryblocks}}.
  
 <table arduinomemoryblocks> <table arduinomemoryblocks>
 <caption>ATMega328P AVR-GCC Virtual Memory Offsets and their real mapping in hardware</caption> <caption>ATMega328P AVR-GCC Virtual Memory Offsets and their real mapping in hardware</caption>
-^ Memory Type ^ GCC Internal Offset ^ Hardware Address ^ +^ Memory Type               ^ GCC Internal Offset  ^ Hardware Address  
-| Flash | %%0x000000%% | %%0x0000%% | +| Flash                     | %%0x000000%%         | %%0x0000%%        
-| SRAM | %%0x800000%% | %%0x0000%% | +| SRAM (Registers and I/O)  | %%0x800000%%         | %%0x0000%%        
-| SRAM (Internal) | %%0x800100%% | %%0x0100%% | +| SRAM (Internal)           | %%0x800100%%         | %%0x0100%%        
-| EEPROM | %%0x810000%% | %%0x0000%% |+| EEPROM                    | %%0x810000%%         | %%0x0000%%        |
 </table> </table>
  
-<note tip>In some of the following chapters, we sometimes present a "naked" code with the use of ''.org 0x0100'' for simplicity. It is only when the code contains no variables, and everything is stored in Flash.</note>+<note tip>In some of the following chapters, we sometimes present a "naked" code without sections, with the use of e.g. ''.org 0x0100'' for simplicity. It is only when the code contains no variables in RAM, it does not use EEPROM, and everything is stored in Flash.</note>
  
-To summarise briefly, the most common scenario is that the code is intended to land in Flash memory, while variables are in SRAM. Appropriate '.orginstructions ensure the correct placement of the following content. +To summarise briefly, the most common scenario is that the code is intended to land in Flash memory, while variables are in SRAM. Appropriate ''.section'directives ensure the correct placement of the following content. 
-It is possible to write code without using sections, but that makes the code unnecessarily complicated. Whenever you use variable declarations, it is advised to use sections to make the code cleaner and easier to understand. If your code is as simple as setting a GPIO out and one does not use variables (everything is in flash), then you may abandon ''.section'' declarations.+It is possible to write code without using sections, but that makes the code unnecessarily complicated. Whenever you use variable declarations, it is advised to use sections to make the code cleaner and easier to understand.\\ If your code is as simple as setting a GPIO out and one does not use variables (everything is in flash), then you may abandon ''.section'' declarations.
  
 The sample code below declares a 16-bit value named 'analogue_value' stored in SRAM (RAM). Note use of ''.section'': The sample code below declares a 16-bit value named 'analogue_value' stored in SRAM (RAM). Note use of ''.section'':
Line 71: Line 68:
 .org 0x100              ; Set SRAM start address manually .org 0x100              ; Set SRAM start address manually
 analogue_value: analogue_value:
-    .skip 2             ; 16-bit variable+    .skip 2             ; 16-bit variable uninitialised
  
 .section .text .section .text
Line 90: Line 87:
 </code> </code>
  
-<note warning>If you declare, e.g. a string in the ''.data'' section in C++ code, the C++ on boot program loads copies all pre-initialised variables from flash to memory. But here, we use pure assembler, and this process is not triggered; the result is that your string variable existing in the SRAM will contain garbage, not the actual string that you're declaring, even if everything looks OK on the source code level. Because of it, and mostly because of the very limited RAM, keep strings and pre-initialised variables in flash and assume ALL variables declared in ''.data'' as uninitialised.</note>+<note warning>If you declare, e.g. a string in the ''.data'' section in C++ code, the C++ on boot program loads copies all pre-initialised variables from flash to memory. But here, we use pure assembler, and this process is not triggered; the result is that your string variable existing in the SRAM will contain garbage, not the actual string that you're declaring, even if everything looks OK on the source code level. Because of it, and mostly because of the very limited RAM, keep strings and constant variables in flash and assume ALL variables declared in ''.data'' as uninitialised.</note>
 ===== GPIO and Ports ===== ===== GPIO and Ports =====
  
Line 107: Line 104:
 </figure> </figure>
  
-<note tip>Some GPIOs have extra features (as presented on figure {{ref>arduinoports}}), such as hardware-accelerated PWM, I2C, Serial or SPI. PWM is useful for simulating an analogue output, e.g., to control LED brightness, as we show in the following sections.</note>+<note tip>Some GPIOs have extra features (as presented in figure {{ref>arduinoports}}), such as hardware-accelerated PWM, I2C, Serial or SPI. PWM is useful for simulating an analogue output, e.g., to control LED brightness, as we show in the following sections.</note>
  
-**IO Registers **\\+**I/O Registers **\\
 Each Port has assigned three 8-bit registers (there are 9 in total then): Each Port has assigned three 8-bit registers (there are 9 in total then):
   * DDRx (Data Direction Register): there are 3 of those registers, one per Port (B, C, D): DDRB, DDRC and DDRD. This registers configures GPIO as Input (0) or Output (1). Configuration is done "per bit", so it is equivalent to controlling each GPIO individually.   * DDRx (Data Direction Register): there are 3 of those registers, one per Port (B, C, D): DDRB, DDRC and DDRD. This registers configures GPIO as Input (0) or Output (1). Configuration is done "per bit", so it is equivalent to controlling each GPIO individually.
Line 178: Line 175:
  
 A common scenario for manual control of the GPIO pin is to first set either the GPIO is input or output (using the correct DDRx register), then either set (''SBI''), reset (''CBI''), check (''SBIS'', ''SBIC''), read the whole register (''IN'') or write the whole register (''OUT''). A common scenario for manual control of the GPIO pin is to first set either the GPIO is input or output (using the correct DDRx register), then either set (''SBI''), reset (''CBI''), check (''SBIS'', ''SBIC''), read the whole register (''IN'') or write the whole register (''OUT'').
-<note tip>''IN'' and ''OUT'' instructions operate on whole, 8-bit registers rather than on single bits. Those are general-purpose instructions that cover the entire range of IO registers (0-63), in addition to the aforementioned DDRx, PORTx, and PINx registers. Operating on multiple bits (8 bits) is faster than setting or reading them individually.</note>+<note tip>''IN'' and ''OUT'' instructions operate on whole, 8-bit registers rather than on single bits. Those are general-purpose instructions that cover the entire range of I/O registers (0-63), in addition to the aforementioned DDRx, PORTx, and PINx registers. Operating on multiple bits (8 bits) is faster than setting or reading them individually.</note>
  
  
Line 412: Line 409:
 </code> </code>
  
-The function is called when interrupt INT0 (on the falling edge of GPIO 2) occurs, and it simply toggles PB5 (GPIO 13, built-in LED). This is a trick in AVR that simplifies code: the classical read->swap->write is replaced by a single ''sbi'' instruction call, which, in the context of the GPIO registers, toggles the selected bit.+The function is called when interrupt INT0 (on the falling edge of GPIO 2) occurs, and it simply toggles PB5 (GPIO 13, built-in LED). This is a trick in AVR that simplifies code: the classical read->swap->write is replaced by a single ''sbi'' instruction, which, in the context of the GPIO registers, toggles the selected bit.
 <code asm> <code asm>
 ; --- Interrupt Service Routine --- ; --- Interrupt Service Routine ---
Line 474: Line 471:
 Where **Fcpu** is 16MHz for regular Arduino Uno (AtMega 328P). Note that this calculation yields ~9615 bps, not exactly 9600 bps. A tolerance of up to 2% is acceptable (here, it is 0.16%). Where **Fcpu** is 16MHz for regular Arduino Uno (AtMega 328P). Note that this calculation yields ~9615 bps, not exactly 9600 bps. A tolerance of up to 2% is acceptable (here, it is 0.16%).
  
-Next step is to enable UART:+Next step is to configure frame format (8 bits, no parity, 1 stop bit, shortly 8N1 - the most common case):
 <code asm> <code asm>
-ldi r16, (1 << TXEN0+ldi r18, (1<<UCSZ01) | (1<<UCSZ00
-sts UCSR0Br16+sts UCSR0Cr18
 </code> </code>
-and configure frame format (8 bits, no parity, 1 stop bit, shortly 8N1 - the most common case):+ 
 +and enable the UART:
 <code asm> <code asm>
 ldi r16, (1 << TXEN0) ldi r16, (1 << TXEN0)
 sts UCSR0B, r16 sts UCSR0B, r16
 </code> </code>
-Now it is time to send the string to the transmitter, byte by byte. Pointer to the string is loaded to Z register (ZH and ZL respectively) using ''ldi''. The string is processed character by character until it encounters 0 (the end of the string).+ 
 +Now it is time to send the string to the transmitter, byte by byte. A pointer to the string is loaded into the Z register (ZH and ZLrespectively) using ''ldi''. The string is processed character by character until it encounters 0 (the end of the string).
 <code asm> <code asm>
 main: main:
Line 531: Line 530:
  
 <table arduinotimerprescalers> <table arduinotimerprescalers>
-<caption>Prescalers and related frequentues and periods for typical 16MHz clock</caption>+<caption>Prescalers and related frequenties and periods for typical 16MHz clock</caption>
 ^ Prescaler ^ Frequency ^ Period (Tick Speed) ^ ^ Prescaler ^ Frequency ^ Period (Tick Speed) ^
 | 1 | 16 MHz | 0.0625 µs | | 1 | 16 MHz | 0.0625 µs |
Line 579: Line 578:
  
 ** The Notification **\\ ** The Notification **\\
-These registers are to control the timer-based interrupt notification system. We do not use interrupts for PWM; thereforethis description is omitted.+These registers are to control the timer-based interrupt notification system. We do not use interrupts for PWM, but they are necessary for handling timer-based tasks.
  
 <table arduinothemanagertimer> <table arduinothemanagertimer>
Line 965: Line 964:
  
 Start conversion by setting the ADSC bit (6) of ADSCRA to 1.  Start conversion by setting the ADSC bit (6) of ADSCRA to 1. 
-ADC requires some time to read the value and complete the conversion (it is based on a capacitor); thus, when it is ready to read, the ADSC bit is cleared by the ADC hardware.+ADC requires some time to complete the conversion; thus, when the result is ready to read, the ADSC bit is cleared by the ADC hardware.
 Here, we do not use any interrupts, just dummy pulling. Here, we do not use any interrupts, just dummy pulling.
 <code asm> <code asm>
Line 1001: Line 1000:
  
 ** Speed vs Quality ** ** Speed vs Quality **
-ADC converts an analogue value to its digital representation using a capacitor. Charging and discharging of the capacitor require time and depend on the impedance of the analogue signal's input source. The general rule says that the faster the conversion, the lower the quality and the higher the error ratio. Conversion speed can be controlled using a prescaler value (bits ADPS2, ADPS1, and ADPS0 of the ADCSRA register). The prescaler divides the clock frequency (16MHz) to slow down the conversion process. Prescaler value and related conversion speed and time is presented in table {{ref>arduinoadcprescaler}}.+ADC converts an analogue value to its digital representation using a method called successive approximation, measuring the voltage stored in a capacitor. Charging and discharging of the capacitor require time and depend on the impedance of the analogue signal's input source. The general rule says that the faster the conversion, the lower the quality and the higher the error ratio. Conversion speed can be controlled using a prescaler value (bits ADPS2, ADPS1, and ADPS0 of the ADCSRA register). The prescaler divides the clock frequency (16MHz) to slow down the conversion process. Prescaler value and related conversion speed and time are presented in table {{ref>arduinoadcprescaler}}.
  
 <table arduinoadcprescaler> <table arduinoadcprescaler>
Line 1044: Line 1043:
  
 <figure avrarduinocallingconventions> <figure avrarduinocallingconventions>
-{{:en:multiasm:exercisesbook:avr_registers.png?600|}}+{{:en:multiasm:exercisesbook:avr_registers.png?660|}}
 <caption>AVR GCC ABI calling standard</caption> <caption>AVR GCC ABI calling standard</caption>
 </figure> </figure>
Line 1198: Line 1197:
 To use a string (e.g. constant) that is stored in Flash, convert this line: To use a string (e.g. constant) that is stored in Flash, convert this line:
 ''ld   r18, Z+'' -> ''lpm   r18, Z+''.</note> ''ld   r18, Z+'' -> ''lpm   r18, Z+''.</note>
-<note important>If you run this function in a loop, give it a delay after each Serial port sending session is finished to let the hardware clean buffers. </note>+<note important>If you run this function in a loop, add a delay after each Serial port sending session to let the hardware clear buffers. </note>
  
 ** Delay (in ms) **\\ ** Delay (in ms) **\\
en/multiasm/exercisesbook/arduinouno.1777878323.txt.gz · Last modified: by pczekalski
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