Sophie

Sophie

distrib > Mageia > 5 > x86_64 > by-pkgid > 670e15010155dd6ee55a5c219435beb1 > files > 8

avr-libc-docs-1.7.1-8.mga5.noarch.rpm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
    <title>avr-libc: avr-libc and assembler programs</title>
    <link href="dox.css" rel="stylesheet" type="text/css">
  </head>
<body>
<center>
<table width="80%">
  <tr>
    <td align="left"><a href="http://www.nongnu.org/avr-libc/">AVR Libc Home Page</a></td>
    <td align="center" colspan=4><img src="avrs.png" alt="AVRs" align="middle" border="0"></td>
    <td align="right"><a href="https://savannah.nongnu.org/projects/avr-libc/">AVR Libc Development Pages</a></td>
  </tr>
  <tr>
    <td align="center" width="13%"><a href="index.html">Main Page</a></td>
    <td align="center" width="13%"><a href="pages.html">User Manual</a></td>
    <td align="center" width="13%"><a href="modules.html">Library Reference</a></td>
    <td align="center" width="13%"><a href="FAQ.html">FAQ</a></td>
    <td align="center" width="13%"><a href="globals.html">Alphabetical Index</a></td>
    <td align="center" width="13%"><a href="group__demos.html">Example Projects</a></td>
  </tr>
</table>
</center>
<hr width="80%">
<!-- Generated by Doxygen 1.7.2 -->
<div class="header">
  <div class="headertitle">
<h1>avr-libc and assembler programs </h1>  </div>
</div>
<div class="contents">
<h2><a class="anchor" id="ass_intro"></a>
Introduction</h2>
<p>There might be several reasons to write code for AVR microcontrollers using plain assembler source code. Among them are:</p>
<ul>
<li>Code for devices that do not have RAM and are thus not supported by the C compiler.</li>
<li>Code for very time-critical applications.</li>
<li>Special tweaks that cannot be done in C.</li>
</ul>
<p>Usually, all but the first could probably be done easily using the <a class="el" href="inline_asm.html">inline assembler</a> facility of the compiler.</p>
<p>Although avr-libc is primarily targeted to support programming AVR microcontrollers using the C (and C++) language, there's limited support for direct assembler usage as well. The benefits of it are:</p>
<ul>
<li>Use of the C preprocessor and thus the ability to use the same symbolic constants that are available to C programs, as well as a flexible macro concept that can use any valid C identifier as a macro (whereas the assembler's macro concept is basically targeted to use a macro in place of an assembler instruction).</li>
<li>Use of the runtime framework like automatically assigning interrupt vectors. For devices that have RAM, <a class="el" href="mem_sections.html#sec_dot_init">initializing the RAM variables</a> can also be utilized.</li>
</ul>
<h2><a class="anchor" id="ass_tools"></a>
Invoking the compiler</h2>
<p>For the purpose described in this document, the assembler and linker are usually not invoked manually, but rather using the C compiler frontend (<code>avr-gcc</code>) that in turn will call the assembler and linker as required.</p>
<p>This approach has the following advantages:</p>
<ul>
<li>There is basically only one program to be called directly, <code>avr-gcc</code>, regardless of the actual source language used.</li>
<li>The invokation of the C preprocessor will be automatic, and will include the appropriate options to locate required include files in the filesystem.</li>
<li>The invokation of the linker will be automatic, and will include the appropriate options to locate additional libraries as well as the application start-up code (<code>crt</code><em>XXX</em><code>.o</code>) and linker script.</li>
</ul>
<p>Note that the invokation of the C preprocessor will be automatic when the filename provided for the assembler file ends in <code></code>.S (the capital letter "s"). This would even apply to operating systems that use case-insensitive filesystems since the actual decision is made based on the case of the filename suffix given on the command-line, not based on the actual filename from the file system.</p>
<p>Alternatively, the language can explicitly be specified using the <code>-x assembler-with-cpp</code> option.</p>
<h2><a class="anchor" id="ass_example"></a>
Example program</h2>
<p>The following annotated example features a simple 100 kHz square wave generator using an AT90S1200 clocked with a 10.7 MHz crystal. Pin PD6 will be used for the square wave output.</p>
<div class="fragment"><pre class="fragment"><span class="preprocessor">#include &lt;<a class="code" href="io_8h.html">avr/io.h</a>&gt;</span>             ; Note [1]

work    =       16              ; Note [2]
tmp     =       17

inttmp  =       19

intsav  =       0

SQUARE  =       PD6             ; Note [3]

                                ; Note [4]:
tmconst= 10700000 / 200000      ; 100 kHz =&gt; 200000 edges/s
fuzz=   8                       ; # clocks in <a class="code" href="group__avr__interrupts.html#gad28590624d422cdf30d626e0a506255f">ISR</a> until TCNT0 is <span class="keyword">set</span>

        .section .text

        .global main                            ; Note [5]
main:
        rcall   ioinit
1:
        rjmp    1b                              ; Note [6]

        .global TIMER0_OVF_vect                 ; Note [7]
TIMER0_OVF_vect:
        ldi     inttmp, 256 - tmconst + fuzz
        out     _SFR_IO_ADDR(TCNT0), inttmp     ; Note [8]

        in      intsav, _SFR_IO_ADDR(SREG)      ; Note [9]

        sbic    _SFR_IO_ADDR(PORTD), SQUARE
        rjmp    1f
        <a class="code" href="group__deprecated__items.html#ga014ef751e83f97569c06f3cdd888f3f7">sbi</a>     _SFR_IO_ADDR(PORTD), SQUARE
        rjmp    2f
1:      <a class="code" href="group__deprecated__items.html#ga08ee265dc07048dbb5a8b6c84551d520">cbi</a>     _SFR_IO_ADDR(PORTD), SQUARE
2:

        out     _SFR_IO_ADDR(SREG), intsav
        <a class="code" href="group__avr__interrupts.html#ga3b991e8168db8fc866e31f9a6d10533b">reti</a>

ioinit:
        <a class="code" href="group__deprecated__items.html#ga014ef751e83f97569c06f3cdd888f3f7">sbi</a>     _SFR_IO_ADDR(DDRD), SQUARE

        ldi     work, <a class="code" href="group__avr__sfr.html#ga11643f271076024c395a93800b3d9546">_BV</a>(TOIE0)
        out     _SFR_IO_ADDR(TIMSK), work

        ldi     work, <a class="code" href="group__avr__sfr.html#ga11643f271076024c395a93800b3d9546">_BV</a>(CS00)         ; tmr0:  CK/1
        out     _SFR_IO_ADDR(TCCR0), work

        ldi     work, 256 - tmconst
        out     _SFR_IO_ADDR(TCNT0), work

        <a class="code" href="group__avr__interrupts.html#gaad5ebd34cb344c26ac87594f79b06b73">sei</a>

        ret

        .global __vector_default                ; Note [10]
__vector_default:
        <a class="code" href="group__avr__interrupts.html#ga3b991e8168db8fc866e31f9a6d10533b">reti</a>

        .end
</pre></div><dl class="user"><dt><b>Note [1]</b></dt><dd></dd></dl>
<p>As in C programs, this includes the central processor-specific file containing the IO port definitions for the device. Note that not all include files can be included into assembler sources.</p>
<dl class="user"><dt><b>Note [2]</b></dt><dd></dd></dl>
<p>Assignment of registers to symbolic names used locally. Another option would be to use a C preprocessor macro instead:</p>
<div class="fragment"><pre class="fragment"><span class="preprocessor"> #define work 16 </span>
</pre></div><dl class="user"><dt><b>Note [3]</b></dt><dd></dd></dl>
<p>Our bit number for the square wave output. Note that the right-hand side consists of a CPP macro which will be substituted by its value (6 in this case) before actually being passed to the assembler.</p>
<dl class="user"><dt><b>Note [4]</b></dt><dd></dd></dl>
<p>The assembler uses integer operations in the host-defined integer size (32 bits or longer) when evaluating expressions. This is in contrast to the C compiler that uses the C type <code>int</code> by default in order to calculate constant integer expressions. <br/>
 In order to get a 100 kHz output, we need to toggle the PD6 line 200000 times per second. Since we use timer 0 without any prescaling options in order to get the desired frequency and accuracy, we already run into serious timing considerations: while accepting and processing the timer overflow interrupt, the timer already continues to count. When pre-loading the <code>TCCNT0</code> register, we therefore have to account for the number of clock cycles required for interrupt acknowledge and for the instructions to reload <code>TCCNT0</code> (4 clock cycles for interrupt acknowledge, 2 cycles for the jump from the interrupt vector, 2 cycles for the 2 instructions that reload <code>TCCNT0</code>). This is what the constant <code>fuzz</code> is for.</p>
<dl class="user"><dt><b>Note [5]</b></dt><dd></dd></dl>
<p>External functions need to be declared to be <code></code>.global. <code>main</code> is the application entry point that will be jumped to from the ininitalization routine in <code>crts1200.o</code>.</p>
<dl class="user"><dt><b>Note [6]</b></dt><dd></dd></dl>
<p>The main loop is just a single jump back to itself. Square wave generation itself is completely handled by the timer 0 overflow interrupt service. A <code>sleep</code> instruction (using idle mode) could be used as well, but probably would not conserve much energy anyway since the interrupt service is executed quite frequently.</p>
<dl class="user"><dt><b>Note [7]</b></dt><dd></dd></dl>
<p>Interrupt functions can get the <a class="el" href="group__avr__interrupts.html#avr_signames">usual names</a> that are also available to C programs. The linker will then put them into the appropriate interrupt vector slots. Note that they must be declared <code></code>.global in order to be acceptable for this purpose. This will only work if <code>&lt;<a class="el" href="io_8h.html">avr/io.h</a>&gt;</code> has been included. Note that the assembler or linker have no chance to check the correct spelling of an interrupt function, so it should be double-checked. (When analyzing the resulting object file using <code>avr-objdump</code> or <code>avr-nm</code>, a name like <code>__vector_<em>N</em></code> should appear, with <em>N</em> being a small integer number.)</p>
<dl class="user"><dt><b>Note [8]</b></dt><dd></dd></dl>
<p>As explained in the section about <a class="el" href="group__avr__sfr__notes.html">special function registers</a>, the actual IO port address should be obtained using the macro <code>_SFR_IO_ADDR</code>. (The AT90S1200 does not have RAM thus the memory-mapped approach to access the IO registers is not available. It would be slower than using <code>in</code> / <code>out</code> instructions anyway.) <br/>
 Since the operation to reload <code>TCCNT0</code> is time-critical, it is even performed before saving <code>SREG</code>. Obviously, this requires that the instructions involved would not change any of the flag bits in <code>SREG</code>.</p>
<p><a class="anchor" id="ass_isr"></a> </p>
<dl class="user"><dt><b>Note [9]</b></dt><dd></dd></dl>
<p>Interrupt routines must not clobber the global CPU state. Thus, it is usually necessary to save at least the state of the flag bits in <code>SREG</code>. (Note that this serves as an example here only since actually, all the following instructions would not modify <code>SREG</code> either, but that's not commonly the case.) <br/>
 Also, it must be made sure that registers used inside the interrupt routine do not conflict with those used outside. In the case of a RAM-less device like the AT90S1200, this can only be done by agreeing on a set of registers to be used exclusively inside the interrupt routine; there would not be any other chance to "save" a register anywhere. <br/>
 If the interrupt routine is to be linked together with C modules, care must be taken to follow the <a class="el" href="FAQ.html#faq_reg_usage">register usage guidelines</a> imposed by the C compiler. Also, any register modified inside the interrupt sevice needs to be saved, usually on the stack.</p>
<dl class="user"><dt><b>Note [10]</b></dt><dd></dd></dl>
<p>As explained in <a class="el" href="group__avr__interrupts.html">Interrupts</a>, a global "catch-all" interrupt handler that gets all unassigned interrupt vectors can be installed using the name <code>__vector_default</code>. This must be <code></code>.global, and obviously, should end in a <code>reti</code> instruction. (By default, a jump to location 0 would be implied instead.)</p>
<h2><a class="anchor" id="ass_pseudoops"></a>
Pseudo-ops and operators</h2>
<p>The available pseudo-ops in the assembler are described in the GNU assembler (gas) manual. The manual can be found online as part of the current binutils release under <a href="http://sources.redhat.com/binutils/.">http://sources.redhat.com/binutils/.</a></p>
<p>As gas comes from a Unix origin, its pseudo-op and overall assembler syntax is slightly different than the one being used by other assemblers. Numeric constants follow the C notation (prefix <code>0x</code> for hexadecimal constants), expressions use a C-like syntax.</p>
<p>Some common pseudo-ops include:</p>
<ul>
<li><code></code>.byte allocates single byte constants</li>
</ul>
<ul>
<li><code></code>.ascii allocates a non-terminated string of characters</li>
</ul>
<ul>
<li><code></code>.asciz allocates a \0-terminated string of characters (C string)</li>
</ul>
<ul>
<li><code></code>.data switches to the .data section (initialized RAM variables)</li>
</ul>
<ul>
<li><code></code>.text switches to the .text section (code and ROM constants)</li>
</ul>
<ul>
<li><code></code>.set declares a symbol as a constant expression (identical to <code></code>.equ)</li>
</ul>
<ul>
<li><code></code>.global (or <code></code>.globl) declares a public symbol that is visible to the linker (e. g. function entry point, global variable)</li>
</ul>
<ul>
<li><code></code>.extern declares a symbol to be externally defined; this is effectively a comment only, as gas treats all undefined symbols it encounters as globally undefined anyway</li>
</ul>
<p>Note that <code></code>.org is available in gas as well, but is a fairly pointless pseudo-op in an assembler environment that uses relocatable object files, as it is the linker that determines the final position of some object in ROM or RAM.</p>
<p>Along with the architecture-independent standard operators, there are some AVR-specific operators available which are unfortunately not yet described in the official documentation. The most notable operators are:</p>
<ul>
<li><code>lo8</code> Takes the least significant 8 bits of a 16-bit integer</li>
</ul>
<ul>
<li><code>hi8</code> Takes the most significant 8 bits of a 16-bit integer</li>
</ul>
<ul>
<li><code>pm</code> Takes a program-memory (ROM) address, and converts it into a RAM address. This implies a division by 2 as the AVR handles ROM addresses as 16-bit words (e.g. in an <code>IJMP</code> or <code>ICALL</code> instruction), and can also handle relocatable symbols on the right-hand side.</li>
</ul>
<p>Example: </p>
<div class="fragment"><pre class="fragment">
	ldi	r24, lo8(pm(somefunc))
	ldi	r25, hi8(pm(somefunc))
	call	something
</pre></div><p>This passes the address of function <code>somefunc</code> as the first parameter to function <code>something</code>. </p>
</div>

<hr width="80%">
<p><center>Automatically generated by Doxygen 1.7.2 on Wed Feb 16 2011.</center></p>

</body>
</html>