update docs
[web/altusmetrum] / AltOS / doc / altos.html
index f51e0a7b36896f07121eec5cd5cd9291926b10fb..ce62d13c24c3118559d5276d0994d0e23d78f615 100644 (file)
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>AltOS</title><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book"><div class="titlepage"><div><div><h1 class="title"><a name="idm6266992"></a>AltOS</h1></div><div><h2 class="subtitle">Altos Metrum Operating System</h2></div><div><div class="author"><h3 class="author"><span class="firstname">Keith</span> <span class="surname">Packard</span></h3></div></div><div><p class="copyright">Copyright © 2010 Keith Packard</p></div><div><div class="legalnotice"><a name="idp48003248"></a><p>
-        This document is released under the terms of the
-        <a class="ulink" href="http://creativecommons.org/licenses/by-sa/3.0/" target="_top">
-          Creative Commons ShareAlike 3.0
-        </a>
-        license.
-      </p></div></div><div><div class="revhistory"><table style="border-style:solid; width:100%;" summary="Revision History"><tr><th align="left" valign="top" colspan="2"><b>Revision History</b></th></tr><tr><td align="left">Revision 1.1</td><td align="left">05 November 2012</td></tr><tr><td align="left" colspan="2">Portable version</td></tr><tr><td align="left">Revision 0.1</td><td align="left">22 November 2010</td></tr><tr><td align="left" colspan="2">Initial content</td></tr></table></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="chapter"><a href="#idp47883536">1. Overview</a></span></dt><dt><span class="chapter"><a href="#idp48965696">2. AltOS Porting Layer</a></span></dt><dd><dl><dt><span class="section"><a href="#idp48967200">1. Low-level CPU operations</a></span></dt><dd><dl><dt><span class="section"><a href="#idp48968432">1.1. ao_arch_block_interrupts/ao_arch_release_interrupts</a></span></dt><dt><span class="section"><a href="#idp48970480">1.2. ao_arch_save_regs, ao_arch_save_stack,
-       ao_arch_restore_stack</a></span></dt><dt><span class="section"><a href="#idp53013312">1.3. ao_arch_wait_interupt</a></span></dt></dl></dd><dt><span class="section"><a href="#idp53015856">2. GPIO operations</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53017024">2.1. GPIO setup</a></span></dt><dt><span class="section"><a href="#idp53026240">2.2. Reading and writing GPIO pins</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#idp47889728">3. Programming the 8051 with SDCC</a></span></dt><dd><dl><dt><span class="section"><a href="#idp47891824">1. 8051 memory spaces</a></span></dt><dd><dl><dt><span class="section"><a href="#idp47893760">1.1. __data</a></span></dt><dt><span class="section"><a href="#idp47895936">1.2. __idata</a></span></dt><dt><span class="section"><a href="#idp47897376">1.3. __xdata</a></span></dt><dt><span class="section"><a href="#idp47898784">1.4. __pdata</a></span></dt><dt><span class="section"><a href="#idp47900288">1.5. __code</a></span></dt><dt><span class="section"><a href="#idp47901728">1.6. __bit</a></span></dt><dt><span class="section"><a href="#idp47903232">1.7. __sfr, __sfr16, __sfr32, __sbit</a></span></dt></dl></dd><dt><span class="section"><a href="#idp47904768">2. Function calls on the 8051</a></span></dt><dd><dl><dt><span class="section"><a href="#idp47906240">2.1. __reentrant functions</a></span></dt><dt><span class="section"><a href="#idp47908432">2.2. Non __reentrant functions</a></span></dt><dt><span class="section"><a href="#idp47910576">2.3. __interrupt functions</a></span></dt><dt><span class="section"><a href="#idp47912144">2.4. __critical functions and statements</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#idp47914208">4. Task functions</a></span></dt><dd><dl><dt><span class="section"><a href="#idp47915264">1. ao_add_task</a></span></dt><dt><span class="section"><a href="#idp47917520">2. ao_exit</a></span></dt><dt><span class="section"><a href="#idp53850448">3. ao_sleep</a></span></dt><dt><span class="section"><a href="#idp53854048">4. ao_wakeup</a></span></dt><dt><span class="section"><a href="#idp53857040">5. ao_alarm</a></span></dt><dt><span class="section"><a href="#idp53860224">6. ao_start_scheduler</a></span></dt><dt><span class="section"><a href="#idp53862192">7. ao_clock_init</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idp53864160">5. Timer Functions</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53865664">1. ao_time</a></span></dt><dt><span class="section"><a href="#idp53867648">2. ao_delay</a></span></dt><dt><span class="section"><a href="#idp53869488">3. ao_timer_set_adc_interval</a></span></dt><dt><span class="section"><a href="#idp53871536">4. ao_timer_init</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idp53873728">6. AltOS Mutexes</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53875600">1. ao_mutex_get</a></span></dt><dt><span class="section"><a href="#idp53877440">2. ao_mutex_put</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idp53879408">7. DMA engine</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53882848">1. CC1111 DMA Engine</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53883520">1.1. ao_dma_alloc</a></span></dt><dt><span class="section"><a href="#idp53885696">1.2. ao_dma_set_transfer</a></span></dt><dt><span class="section"><a href="#idp53887968">1.3. ao_dma_start</a></span></dt><dt><span class="section"><a href="#idp53889984">1.4. ao_dma_trigger</a></span></dt><dt><span class="section"><a href="#idp53891824">1.5. ao_dma_abort</a></span></dt></dl></dd><dt><span class="section"><a href="#idp53893968">2. STM32L DMA Engine</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53894640">2.1. ao_dma_alloc</a></span></dt><dt><span class="section"><a href="#idp53896480">2.2. ao_dma_set_transfer</a></span></dt><dt><span class="section"><a href="#idp53898736">2.3. ao_dma_set_isr</a></span></dt><dt><span class="section"><a href="#idp53900816">2.4. ao_dma_start</a></span></dt><dt><span class="section"><a href="#idp53903120">2.5. ao_dma_done_transfer</a></span></dt><dt><span class="section"><a href="#idp53905104">2.6. ao_dma_abort</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#idp53907376">8. Stdio interface</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53908880">1. putchar</a></span></dt><dt><span class="section"><a href="#idp53910768">2. getchar</a></span></dt><dt><span class="section"><a href="#idp53912864">3. flush</a></span></dt><dt><span class="section"><a href="#idp53914896">4. ao_add_stdio</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idp53918848">9. Command line interface</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53920256">1. ao_cmd_register</a></span></dt><dt><span class="section"><a href="#idp53928816">2. ao_cmd_lex</a></span></dt><dt><span class="section"><a href="#idp53930896">3. ao_cmd_put16</a></span></dt><dt><span class="section"><a href="#idp53932736">4. ao_cmd_put8</a></span></dt><dt><span class="section"><a href="#idp53934624">5. ao_cmd_white</a></span></dt><dt><span class="section"><a href="#idp53936688">6. ao_cmd_hex</a></span></dt><dt><span class="section"><a href="#idp53938736">7. ao_cmd_decimal</a></span></dt><dt><span class="section"><a href="#idp53940832">8. ao_match_word</a></span></dt><dt><span class="section"><a href="#idp53942912">9. ao_cmd_init</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idp53945136">10. USB target device</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53947360">1. ao_usb_flush</a></span></dt><dt><span class="section"><a href="#idp53949504">2. ao_usb_putchar</a></span></dt><dt><span class="section"><a href="#idp53951680">3. ao_usb_pollchar</a></span></dt><dt><span class="section"><a href="#idp53953824">4. ao_usb_getchar</a></span></dt><dt><span class="section"><a href="#idp53955840">5. ao_usb_disable</a></span></dt><dt><span class="section"><a href="#idp53958672">6. ao_usb_enable</a></span></dt><dt><span class="section"><a href="#idp53960784">7. ao_usb_init</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idp53963008">11. Serial peripherals</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53965024">1. ao_serial_getchar</a></span></dt><dt><span class="section"><a href="#idp53967056">2. ao_serial_putchar</a></span></dt><dt><span class="section"><a href="#idp53969024">3. ao_serial_drain</a></span></dt><dt><span class="section"><a href="#idp53971024">4. ao_serial_set_speed</a></span></dt><dt><span class="section"><a href="#idp53973056">5. ao_serial_init</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idp53975248">12. CC1111 Radio peripheral</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53975920">1. Radio Introduction</a></span></dt><dt><span class="section"><a href="#idp53983040">2. ao_radio_set_telemetry</a></span></dt><dt><span class="section"><a href="#idp53985200">3. ao_radio_set_packet</a></span></dt><dt><span class="section"><a href="#idp53987360">4. ao_radio_set_rdf</a></span></dt><dt><span class="section"><a href="#idp53989600">5. ao_radio_idle</a></span></dt><dt><span class="section"><a href="#idp53991648">6. ao_radio_get</a></span></dt><dt><span class="section"><a href="#idp53993680">7. ao_radio_put</a></span></dt><dt><span class="section"><a href="#idp53995616">8. ao_radio_abort</a></span></dt><dt><span class="section"><a href="#idp53997680">9. Radio Telemetry</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53998976">9.1. ao_radio_send</a></span></dt><dt><span class="section"><a href="#idp54001120">9.2. ao_radio_recv</a></span></dt></dl></dd><dt><span class="section"><a href="#idp54003504">10. Radio Direction Finding</a></span></dt><dd><dl><dt><span class="section"><a href="#idp54004656">10.1. ao_radio_rdf</a></span></dt></dl></dd><dt><span class="section"><a href="#idp54006800">11. Radio Packet Mode</a></span></dt><dd><dl><dt><span class="section"><a href="#idp54008112">11.1. ao_packet_putchar</a></span></dt><dt><span class="section"><a href="#idp54010288">11.2. ao_packet_pollchar</a></span></dt><dt><span class="section"><a href="#idp54012368">11.3. ao_packet_slave_start</a></span></dt><dt><span class="section"><a href="#idp54014320">11.4. ao_packet_slave_stop</a></span></dt><dt><span class="section"><a href="#idp54016160">11.5. ao_packet_slave_init</a></span></dt><dt><span class="section"><a href="#idp54018192">11.6. ao_packet_master_init</a></span></dt></dl></dd></dl></dd></dl></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp47883536"></a>Chapter 1. Overview</h1></div></div></div><p>
-      AltOS is a operating system built for a variety of
-      microcontrollers used in Altus Metrum devices. It has a simple
-      porting layer for each CPU while providing a convenient
-      operating enviroment for the developer. AltOS currently
-      supports three different CPUs:
-      </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
-           STM32L series from ST Microelectronics. This ARM Cortex-M3
-           based microcontroller offers low power consumption and a
-           wide variety of built-in peripherals. Altus Metrum uses
-           this in the TeleMega, MegaDongle and TeleLCO projects.
-         </p></li><li class="listitem"><p>
-           CC1111 from Texas Instruments. This device includes a
-           fabulous 10mW digital RF transceiver along with an
-           8051-compatible processor core and a range of
-           peripherals. This is used in the TeleMetrum, TeleMini,
-           TeleDongle and TeleFire projects which share the need for
-           a small microcontroller and an RF interface.
-         </p></li><li class="listitem"><p>
-           ATmega32U4 from Atmel. This 8-bit AVR microcontroller is
-           one of the many used to create Arduino boards. The 32U4
-           includes a USB interface, making it easy to connect to
-           other computers. Altus Metrum used this in prototypes of
-           the TeleScience and TelePyro boards; those have been
-           switched to the STM32L which is more capable and cheaper.
-         </p></li></ul></div><p>
-      Among the features of AltOS are:
-      </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>Multi-tasking. While microcontrollers often don't
-         provide separate address spaces, it's often easier to write
-         code that operates in separate threads instead of tying
-         everything into one giant event loop.
-         </p></li><li class="listitem"><p>Non-preemptive. This increases latency for thread
-         switching but reduces the number of places where context
-         switching can occur. It also simplifies the operating system
-         design somewhat. Nothing in the target system (rocket flight
-         control) has tight timing requirements, and so this seems like
-         a reasonable compromise.
-         </p></li><li class="listitem"><p>Sleep/wakeup scheduling. Taken directly from ancient
-         Unix designs, these two provide the fundemental scheduling
-         primitive within AltOS.
-         </p></li><li class="listitem"><p>Mutexes. As a locking primitive, mutexes are easier to
-         use than semaphores, at least in my experience.
-         </p></li><li class="listitem"><p>Timers. Tasks can set an alarm which will abort any
-         pending sleep, allowing operations to time-out instead of
-         blocking forever.
-         </p></li></ul></div><p>
-    </p><p>
-      The device drivers and other subsystems in AltOS are
-      conventionally enabled by invoking their _init() function from
-      the 'main' function before that calls
-      ao_start_scheduler(). These functions initialize the pin
-      assignments, add various commands to the command processor and
-      may add tasks to the scheduler to handle the device. A typical
-      main program, thus, looks like:
-      </p><pre class="programlisting">
-       void
-       main(void)
-       {
-               ao_clock_init();
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<meta name="generator" content="Asciidoctor 2.0.10">
+<meta name="author" content="Keith Packard">
+<title>AltOS</title>
+<link rel="stylesheet" href="./am.css">
+</head>
+<body class="book">
+<div id="header">
+<h1>AltOS</h1>
+<div class="details">
+<span id="author" class="author">Keith Packard</span><br>
+<span id="email" class="email"><a href="mailto:keithp@keithp.com">keithp@keithp.com</a></span><br>
+<span id="revdate">29 Sep 2020</span>
+</div>
+<div id="toc" class="toc">
+<div id="toctitle">Table of Contents</div>
+<ul class="sectlevel1">
+<li><a href="#_license">License</a></li>
+<li><a href="#_overview">1. Overview</a></li>
+<li><a href="#_altos_porting_layer">2. AltOS Porting Layer</a>
+<ul class="sectlevel2">
+<li><a href="#_low_level_cpu_operations">2.1. Low-level CPU operations</a></li>
+<li><a href="#_gpio_operations">2.2. GPIO operations</a></li>
+<li><a href="#_8051_memory_spaces">2.3. 8051 memory spaces</a></li>
+<li><a href="#_function_calls_on_the_8051">2.4. Function calls on the 8051</a></li>
+</ul>
+</li>
+<li><a href="#_task_functions">3. Task functions</a>
+<ul class="sectlevel2">
+<li><a href="#_ao_add_task">3.1. ao_add_task</a></li>
+<li><a href="#_ao_exit">3.2. ao_exit</a></li>
+<li><a href="#_ao_sleep">3.3. ao_sleep</a></li>
+<li><a href="#_ao_wakeup">3.4. ao_wakeup</a></li>
+<li><a href="#_ao_alarm">3.5. ao_alarm</a></li>
+<li><a href="#_ao_start_scheduler">3.6. ao_start_scheduler</a></li>
+<li><a href="#_ao_clock_init">3.7. ao_clock_init</a></li>
+</ul>
+</li>
+<li><a href="#_timer_functions">4. Timer Functions</a>
+<ul class="sectlevel2">
+<li><a href="#_ao_time">4.1. ao_time</a></li>
+<li><a href="#_ao_delay">4.2. ao_delay</a></li>
+<li><a href="#_ao_timer_set_adc_interval">4.3. ao_timer_set_adc_interval</a></li>
+<li><a href="#_ao_timer_init">4.4. ao_timer_init</a></li>
+</ul>
+</li>
+<li><a href="#_altos_mutexes">5. AltOS Mutexes</a>
+<ul class="sectlevel2">
+<li><a href="#_ao_mutex_get">5.1. ao_mutex_get</a></li>
+<li><a href="#_ao_mutex_put">5.2. ao_mutex_put</a></li>
+</ul>
+</li>
+<li><a href="#_dma_engine">6. DMA engine</a>
+<ul class="sectlevel2">
+<li><a href="#_cc1111_dma_engine">6.1. CC1111 DMA Engine</a></li>
+<li><a href="#_stm32l_dma_engine">6.2. STM32L DMA Engine</a></li>
+</ul>
+</li>
+<li><a href="#_stdio_interface">7. Stdio interface</a>
+<ul class="sectlevel2">
+<li><a href="#_putchar">7.1. putchar</a></li>
+<li><a href="#_getchar">7.2. getchar</a></li>
+<li><a href="#_flush">7.3. flush</a></li>
+<li><a href="#_ao_add_stdio">7.4. ao_add_stdio</a></li>
+</ul>
+</li>
+<li><a href="#_command_line_interface">8. Command line interface</a>
+<ul class="sectlevel2">
+<li><a href="#_ao_cmd_register">8.1. ao_cmd_register</a></li>
+<li><a href="#_ao_cmd_lex">8.2. ao_cmd_lex</a></li>
+<li><a href="#_ao_cmd_put16">8.3. ao_cmd_put16</a></li>
+<li><a href="#_ao_cmd_put8">8.4. ao_cmd_put8</a></li>
+<li><a href="#_ao_cmd_white">8.5. ao_cmd_white</a></li>
+<li><a href="#_ao_cmd_hex">8.6. ao_cmd_hex</a></li>
+<li><a href="#_ao_cmd_decimal">8.7. ao_cmd_decimal</a></li>
+<li><a href="#_ao_match_word">8.8. ao_match_word</a></li>
+<li><a href="#_ao_cmd_init">8.9. ao_cmd_init</a></li>
+</ul>
+</li>
+<li><a href="#_usb_target_device">9. USB target device</a>
+<ul class="sectlevel2">
+<li><a href="#_ao_usb_flush">9.1. ao_usb_flush</a></li>
+<li><a href="#_ao_usb_putchar">9.2. ao_usb_putchar</a></li>
+<li><a href="#_ao_usb_pollchar">9.3. ao_usb_pollchar</a></li>
+<li><a href="#_ao_usb_getchar">9.4. ao_usb_getchar</a></li>
+<li><a href="#_ao_usb_disable">9.5. ao_usb_disable</a></li>
+<li><a href="#_ao_usb_enable">9.6. ao_usb_enable</a></li>
+<li><a href="#_ao_usb_init">9.7. ao_usb_init</a></li>
+</ul>
+</li>
+<li><a href="#_serial_peripherals">10. Serial peripherals</a>
+<ul class="sectlevel2">
+<li><a href="#_ao_serial_getchar">10.1. ao_serial_getchar</a></li>
+<li><a href="#_ao_serial_putchar">10.2. ao_serial_putchar</a></li>
+<li><a href="#_ao_serial_drain">10.3. ao_serial_drain</a></li>
+<li><a href="#_ao_serial_set_speed">10.4. ao_serial_set_speed</a></li>
+<li><a href="#_ao_serial_init">10.5. ao_serial_init</a></li>
+</ul>
+</li>
+<li><a href="#_cc1111cc1120cc1200_radio_peripheral">11. CC1111/CC1120/CC1200 Radio peripheral</a>
+<ul class="sectlevel2">
+<li><a href="#_radio_introduction">11.1. Radio Introduction</a></li>
+<li><a href="#_ao_radio_set_telemetry">11.2. ao_radio_set_telemetry</a></li>
+<li><a href="#_ao_radio_set_packet">11.3. ao_radio_set_packet</a></li>
+<li><a href="#_ao_radio_set_rdf">11.4. ao_radio_set_rdf</a></li>
+<li><a href="#_ao_radio_idle">11.5. ao_radio_idle</a></li>
+<li><a href="#_ao_radio_get">11.6. ao_radio_get</a></li>
+<li><a href="#_ao_radio_put">11.7. ao_radio_put</a></li>
+<li><a href="#_ao_radio_abort">11.8. ao_radio_abort</a></li>
+<li><a href="#_radio_telemetry">11.9. Radio Telemetry</a></li>
+<li><a href="#_radio_direction_finding">11.10. Radio Direction Finding</a></li>
+<li><a href="#_radio_packet_mode">11.11. Radio Packet Mode</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+<div id="content">
+<div id="preamble">
+<div class="sectionbody">
+<div id="logo" class="imageblock">
+<div class="content">
+<a class="image" href="https://altusmetrum.org"><img src="altusmetrum-oneline.svg" alt="Altus Metrum"></a>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_license">License</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Copyright © 2018 Bdale Garbee and Keith Packard</p>
+</div>
+<div class="paragraph">
+<p>This document is released under the terms of the <a href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons ShareAlike 3.0 License</a></p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_overview">1. Overview</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>AltOS is a operating system built for a variety of
+microcontrollers used in Altus Metrum devices. It has a simple
+porting layer for each CPU while providing a convenient
+operating enviroment for the developer. AltOS currently
+supports three different CPUs:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>STM32L series from ST Microelectronics. This ARM Cortex-M3
+based microcontroller offers low power consumption and a
+wide variety of built-in peripherals. Altus Metrum uses this
+in the TeleMega, MegaDongle and TeleLCO projects.</p>
+</li>
+<li>
+<p>CC1111 from Texas Instruments. This device includes a
+fabulous 10mW digital RF transceiver along with an
+8051-compatible processor core and a range of
+peripherals. This is used in the TeleMetrum, TeleMini,
+TeleDongle and TeleFire projects which share the need for a
+small microcontroller and an RF interface.</p>
+</li>
+<li>
+<p>ATmega32U4 from Atmel. This 8-bit AVR microcontroller is one
+of the many used to create Arduino boards. The 32U4 includes
+a USB interface, making it easy to connect to other
+computers. Altus Metrum used this in prototypes of the
+TeleScience and TelePyro boards; those have been switched to
+the STM32L which is more capable and cheaper.</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>Among the features of AltOS are:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>Multi-tasking. While microcontrollers often don&#8217;t
+provide separate address spaces, it&#8217;s often easier to write
+code that operates in separate threads instead of tying
+everything into one giant event loop.</p>
+</li>
+<li>
+<p>Non-preemptive. This increases latency for thread
+switching but reduces the number of places where context
+switching can occur. It also simplifies the operating system
+design somewhat. Nothing in the target system (rocket flight
+control) has tight timing requirements, and so this seems like
+a reasonable compromise.</p>
+</li>
+<li>
+<p>Sleep/wakeup scheduling. Taken directly from ancient
+Unix designs, these two provide the fundemental scheduling
+primitive within AltOS.</p>
+</li>
+<li>
+<p>Mutexes. As a locking primitive, mutexes are easier to
+use than semaphores, at least in my experience.</p>
+</li>
+<li>
+<p>Timers. Tasks can set an alarm which will abort any
+pending sleep, allowing operations to time-out instead of
+blocking forever.</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>The device drivers and other subsystems in AltOS are
+conventionally enabled by invoking their _init() function from
+the 'main' function before that calls
+ao_start_scheduler(). These functions initialize the pin
+assignments, add various commands to the command processor and
+may add tasks to the scheduler to handle the device. A typical
+main program, thus, looks like:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>void
+main(void)
+{
+       ao_clock_init();
 
-               /* Turn on the LED until the system is stable */
-               ao_led_init(LEDS_AVAILABLE);
-               ao_led_on(AO_LED_RED);
-               ao_timer_init();
-               ao_cmd_init();
-               ao_usb_init();
-               ao_monitor_init(AO_LED_GREEN, TRUE);
-               ao_rssi_init(AO_LED_RED);
-               ao_radio_init();
-               ao_packet_slave_init();
-               ao_packet_master_init();
-               #if HAS_DBG
-               ao_dbg_init();
-               #endif
-               ao_config_init();
-               ao_start_scheduler();
-       }
-      </pre><p>
-      As you can see, a long sequence of subsystems are initialized
-      and then the scheduler is started.
-    </p></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp48965696"></a>Chapter 2. AltOS Porting Layer</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="#idp48967200">1. Low-level CPU operations</a></span></dt><dd><dl><dt><span class="section"><a href="#idp48968432">1.1. ao_arch_block_interrupts/ao_arch_release_interrupts</a></span></dt><dt><span class="section"><a href="#idp48970480">1.2. ao_arch_save_regs, ao_arch_save_stack,
-       ao_arch_restore_stack</a></span></dt><dt><span class="section"><a href="#idp53013312">1.3. ao_arch_wait_interupt</a></span></dt></dl></dd><dt><span class="section"><a href="#idp53015856">2. GPIO operations</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53017024">2.1. GPIO setup</a></span></dt><dt><span class="section"><a href="#idp53026240">2.2. Reading and writing GPIO pins</a></span></dt></dl></dd></dl></div><p>
-      AltOS provides a CPU-independent interface to various common
-      microcontroller subsystems, including GPIO pins, interrupts,
-      SPI, I2C, USB and asynchronous serial interfaces. By making
-      these CPU-independent, device drivers, generic OS and
-      application code can all be written that work on any supported
-      CPU. Many of the architecture abstraction interfaces are
-      prefixed with ao_arch.
-    </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp48967200"></a>1. Low-level CPU operations</h2></div></div></div><p>
-       These primitive operations provide the abstraction needed to
-       run the multi-tasking framework while providing reliable
-       interrupt delivery.
-      </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp48968432"></a>1.1. ao_arch_block_interrupts/ao_arch_release_interrupts</h3></div></div></div><pre class="programlisting">
-         static inline void
-         ao_arch_block_interrupts(void);
-         
-         static inline void
-         ao_arch_release_interrupts(void);
-       </pre><p>
-         These disable/enable interrupt delivery, they may not
-         discard any interrupts. Use these for sections of code that
-         must be atomic with respect to any code run from an
-         interrupt handler.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp48970480"></a>1.2. ao_arch_save_regs, ao_arch_save_stack,
-       ao_arch_restore_stack</h3></div></div></div><pre class="programlisting">
-         static inline void
-         ao_arch_save_regs(void);
+       /* Turn on the LED until the system is stable */
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_RED);
+       ao_timer_init();
+       ao_cmd_init();
+       ao_usb_init();
+       ao_monitor_init(AO_LED_GREEN, TRUE);
+       ao_rssi_init(AO_LED_RED);
+       ao_radio_init();
+       ao_packet_slave_init();
+       ao_packet_master_init();
+#if HAS_DBG
+       ao_dbg_init();
+#endif
+       ao_config_init();
+       ao_start_scheduler();
+}</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>As you can see, a long sequence of subsystems are initialized
+and then the scheduler is started.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_altos_porting_layer">2. AltOS Porting Layer</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>AltOS provides a CPU-independent interface to various common
+microcontroller subsystems, including GPIO pins, interrupts,
+SPI, I2C, USB and asynchronous serial interfaces. By making
+these CPU-independent, device drivers, generic OS and
+application code can all be written that work on any supported
+CPU. Many of the architecture abstraction interfaces are
+prefixed with ao_arch.</p>
+</div>
+<div class="sect2">
+<h3 id="_low_level_cpu_operations">2.1. Low-level CPU operations</h3>
+<div class="paragraph">
+<p>These primitive operations provide the abstraction needed to
+run the multi-tasking framework while providing reliable
+interrupt delivery.</p>
+</div>
+<div class="sect3">
+<h4 id="_ao_arch_block_interruptsao_arch_release_interrupts">2.1.1. ao_arch_block_interrupts/ao_arch_release_interrupts</h4>
+<div class="literalblock">
+<div class="content">
+<pre>static inline void
+ao_arch_block_interrupts(void);
 
-         static inline void
-         ao_arch_save_stack(void);
+static inline void
+ao_arch_release_interrupts(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>These disable/enable interrupt delivery, they may not
+discard any interrupts. Use these for sections of code that
+must be atomic with respect to any code run from an
+interrupt handler.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_arch_save_regs_ao_arch_save_stack_ao_arch_restore_stack">2.1.2. ao_arch_save_regs, ao_arch_save_stack, ao_arch_restore_stack</h4>
+<div class="literalblock">
+<div class="content">
+<pre>static inline void
+ao_arch_save_regs(void);
 
-         static inline void
-         ao_arch_restore_stack(void);
-       </pre><p>
-         These provide all of the support needed to switch between
-         tasks.. ao_arch_save_regs must save all CPU registers to the
-         current stack, including the interrupt enable
-         state. ao_arch_save_stack records the current stack location
-         in the current ao_task structure. ao_arch_restore_stack
-         switches back to the saved stack, restores all registers and
-         branches to the saved return address.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53013312"></a>1.3. ao_arch_wait_interupt</h3></div></div></div><pre class="programlisting">
-         #define ao_arch_wait_interrupt()
-       </pre><p>
-         This stops the CPU, leaving clocks and interrupts
-         enabled. When an interrupt is received, this must wake up
-         and handle the interrupt. ao_arch_wait_interrupt is entered
-         with interrupts disabled to ensure that there is no gap
-         between determining that no task wants to run and idling the
-         CPU. It must sleep the CPU, process interrupts and then
-         disable interrupts again. If the CPU doesn't have any
-         reduced power mode, this must at the least allow pending
-         interrupts to be processed.
-       </p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53015856"></a>2. GPIO operations</h2></div></div></div><p>
-       These functions provide an abstract interface to configure and
-       manipulate GPIO pins.
-      </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53017024"></a>2.1. GPIO setup</h3></div></div></div><p>
-         These macros may be invoked at system initialization time to
-         configure pins as needed for system operation. One tricky
-         aspect is that some chips provide direct access to specific
-         GPIO pins while others only provide access to a whole
-         register full of pins. To support this, the GPIO macros
-         provide both port+bit and pin arguments. Simply define the
-         arguments needed for the target platform and leave the
-         others undefined.
-       </p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idp53018864"></a>2.1.1. ao_enable_output</h4></div></div></div><pre class="programlisting">
-           #define ao_enable_output(port, bit, pin, value)
-         </pre><p>
-           Set the specified port+bit (also called 'pin') for output,
-           initializing to the specified value. The macro must avoid
-           driving the pin with the opposite value if at all
-           possible.
-         </p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idp53020896"></a>2.1.2. ao_enable_input</h4></div></div></div><pre class="programlisting">
-           #define ao_enable_input(port, bit, mode)
-         </pre><p>
-           Sets the specified port/bit to be an input pin. 'mode' is
-           a combination of one or more of the following. Note that
-           some platforms may not support the desired mode. In that
-           case, the value will not be defined so that the program
-           will fail to compile.
-           </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
-               AO_EXTI_MODE_PULL_UP. Apply a pull-up to the pin; a
-               disconnected pin will read as 1.
-</p></li><li class="listitem"><p>
-               AO_EXTI_MODE_PULL_DOWN. Apply a pull-down to the pin;
-               a disconnected pin will read as 0.
-</p></li><li class="listitem"><p>
-               0. Don't apply either a pull-up or pull-down. A
-               disconnected pin will read an undetermined value.
-</p></li></ul></div><p>
-         </p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53026240"></a>2.2. Reading and writing GPIO pins</h3></div></div></div><p>
-         These macros read and write individual GPIO pins.
-       </p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idp53027312"></a>2.2.1. ao_gpio_set</h4></div></div></div><pre class="programlisting">
-           #define ao_gpio_set(port, bit, pin, value)
-         </pre><p>
-           Sets the specified port/bit or pin to the indicated value
-         </p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idp47887392"></a>2.2.2. ao_gpio_get</h4></div></div></div><pre class="programlisting">
-           #define ao_gpio_get(port, bit, pin)
-         </pre><p>
-           Returns either 1 or 0 depending on whether the input to
-           the pin is high or low.
-         </p></div></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp47889728"></a>Chapter 3. Programming the 8051 with SDCC</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="#idp47891824">1. 8051 memory spaces</a></span></dt><dd><dl><dt><span class="section"><a href="#idp47893760">1.1. __data</a></span></dt><dt><span class="section"><a href="#idp47895936">1.2. __idata</a></span></dt><dt><span class="section"><a href="#idp47897376">1.3. __xdata</a></span></dt><dt><span class="section"><a href="#idp47898784">1.4. __pdata</a></span></dt><dt><span class="section"><a href="#idp47900288">1.5. __code</a></span></dt><dt><span class="section"><a href="#idp47901728">1.6. __bit</a></span></dt><dt><span class="section"><a href="#idp47903232">1.7. __sfr, __sfr16, __sfr32, __sbit</a></span></dt></dl></dd><dt><span class="section"><a href="#idp47904768">2. Function calls on the 8051</a></span></dt><dd><dl><dt><span class="section"><a href="#idp47906240">2.1. __reentrant functions</a></span></dt><dt><span class="section"><a href="#idp47908432">2.2. Non __reentrant functions</a></span></dt><dt><span class="section"><a href="#idp47910576">2.3. __interrupt functions</a></span></dt><dt><span class="section"><a href="#idp47912144">2.4. __critical functions and statements</a></span></dt></dl></dd></dl></div><p>
-      The 8051 is a primitive 8-bit processor, designed in the mists
-      of time in as few transistors as possible. The architecture is
-      highly irregular and includes several separate memory
-      spaces. Furthermore, accessing stack variables is slow, and the
-      stack itself is of limited size. While SDCC papers over the
-      instruction set, it is not completely able to hide the memory
-      architecture from the application designer.
-    </p><p>
-      When built on other architectures, the various SDCC-specific
-      symbols are #defined as empty strings so they don't affect the compiler.
-    </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp47891824"></a>1. 8051 memory spaces</h2></div></div></div><p>
-       The __data/__xdata/__code memory spaces below were completely
-       separate in the original 8051 design. In the cc1111, this
-       isn't true&#8212;they all live in a single unified 64kB address
-       space, and so it's possible to convert any address into a
-       unique 16-bit address. SDCC doesn't know this, and so a
-       'global' address to SDCC consumes 3 bytes of memory, 1 byte as
-       a tag indicating the memory space and 2 bytes of offset within
-       that space. AltOS avoids these 3-byte addresses as much as
-       possible; using them involves a function call per byte
-       access. The result is that nearly every variable declaration
-       is decorated with a memory space identifier which clutters the
-       code but makes the resulting code far smaller and more
-       efficient.
-      </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp47893760"></a>1.1. __data</h3></div></div></div><p>
-         The 8051 can directly address these 128 bytes of
-         memory. This makes them precious so they should be
-         reserved for frequently addressed values. Oh, just to
-         confuse things further, the 8 general registers in the
-         CPU are actually stored in this memory space. There are
-         magic instructions to 'bank switch' among 4 banks of
-         these registers located at 0x00 - 0x1F. AltOS uses only
-         the first bank at 0x00 - 0x07, leaving the other 24
-         bytes available for other data.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp47895936"></a>1.2. __idata</h3></div></div></div><p>
-         There are an additional 128 bytes of internal memory
-         that share the same address space as __data but which
-         cannot be directly addressed. The stack normally
-         occupies this space and so AltOS doesn't place any
-         static storage here.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp47897376"></a>1.3. __xdata</h3></div></div></div><p>
-         This is additional general memory accessed through a
-         single 16-bit address register. The CC1111F32 has 32kB
-         of memory available here. Most program data should live
-         in this memory space.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp47898784"></a>1.4. __pdata</h3></div></div></div><p>
-         This is an alias for the first 256 bytes of __xdata
-         memory, but uses a shorter addressing mode with
-         single global 8-bit value for the high 8 bits of the
-         address and any of several 8-bit registers for the low 8
-         bits. AltOS uses a few bits of this memory, it should
-         probably use more.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp47900288"></a>1.5. __code</h3></div></div></div><p>
-         All executable code must live in this address space, but
-         you can stick read-only data here too. It is addressed
-         using the 16-bit address register and special 'code'
-         access opcodes. Anything read-only should live in this space.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp47901728"></a>1.6. __bit</h3></div></div></div><p>
-         The 8051 has 128 bits of bit-addressible memory that
-         lives in the __data segment from 0x20 through
-         0x2f. Special instructions access these bits
-         in a single atomic operation. This isn't so much a
-         separate address space as a special addressing mode for
-         a few bytes in the __data segment.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp47903232"></a>1.7. __sfr, __sfr16, __sfr32, __sbit</h3></div></div></div><p>
-         Access to physical registers in the device use this mode
-         which declares the variable name, it's type and the
-         address it lives at. No memory is allocated for these
-         variables.
-       </p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp47904768"></a>2. Function calls on the 8051</h2></div></div></div><p>
-       Because stack addressing is expensive, and stack space
-       limited, the default function call declaration in SDCC
-       allocates all parameters and local variables in static global
-       memory. Just like fortran. This makes these functions
-       non-reentrant, and also consume space for parameters and
-       locals even when they are not running. The benefit is smaller
-       code and faster execution.
-      </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp47906240"></a>2.1. __reentrant functions</h3></div></div></div><p>
-         All functions which are re-entrant, either due to recursion
-         or due to a potential context switch while executing, should
-         be marked as __reentrant so that their parameters and local
-         variables get allocated on the stack. This ensures that
-         these values are not overwritten by another invocation of
-         the function.
-       </p><p>
-         Functions which use significant amounts of space for
-         arguments and/or local variables and which are not often
-         invoked can also be marked as __reentrant. The resulting
-         code will be larger, but the savings in memory are
-         frequently worthwhile.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp47908432"></a>2.2. Non __reentrant functions</h3></div></div></div><p>
-         All parameters and locals in non-reentrant functions can
-         have data space decoration so that they are allocated in
-         __xdata, __pdata or __data space as desired. This can avoid
-         consuming __data space for infrequently used variables in
-         frequently used functions.
-       </p><p>
-         All library functions called by SDCC, including functions
-         for multiplying and dividing large data types, are
-         non-reentrant. Because of this, interrupt handlers must not
-         invoke any library functions, including the multiply and
-         divide code.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp47910576"></a>2.3. __interrupt functions</h3></div></div></div><p>
-         Interrupt functions are declared with with an __interrupt
-         decoration that includes the interrupt number. SDCC saves
-         and restores all of the registers in these functions and
-         uses the 'reti' instruction at the end so that they operate
-         as stand-alone interrupt handlers. Interrupt functions may
-         call the ao_wakeup function to wake AltOS tasks.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp47912144"></a>2.4. __critical functions and statements</h3></div></div></div><p>
-         SDCC has built-in support for suspending interrupts during
-         critical code. Functions marked as __critical will have
-         interrupts suspended for the whole period of
-         execution. Individual statements may also be marked as
-         __critical which blocks interrupts during the execution of
-         that statement. Keeping critical sections as short as
-         possible is key to ensuring that interrupts are handled as
-         quickly as possible. AltOS doesn't use this form in shared
-         code as other compilers wouldn't know what to do. Use
-         ao_arch_block_interrupts and ao_arch_release_interrupts instead.
-       </p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp47914208"></a>Chapter 4. Task functions</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="#idp47915264">1. ao_add_task</a></span></dt><dt><span class="section"><a href="#idp47917520">2. ao_exit</a></span></dt><dt><span class="section"><a href="#idp53850448">3. ao_sleep</a></span></dt><dt><span class="section"><a href="#idp53854048">4. ao_wakeup</a></span></dt><dt><span class="section"><a href="#idp53857040">5. ao_alarm</a></span></dt><dt><span class="section"><a href="#idp53860224">6. ao_start_scheduler</a></span></dt><dt><span class="section"><a href="#idp53862192">7. ao_clock_init</a></span></dt></dl></div><p>
-      This chapter documents how to create, destroy and schedule AltOS tasks.
-    </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp47915264"></a>1. ao_add_task</h2></div></div></div><pre class="programlisting">
-       void
-       ao_add_task(__xdata struct ao_task * task,
-                   void (*start)(void),
-                   __code char *name);
-      </pre><p>
-       This initializes the statically allocated task structure,
-       assigns a name to it (not used for anything but the task
-       display), and the start address. It does not switch to the
-       new task. 'start' must not ever return; there is no place
-       to return to.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp47917520"></a>2. ao_exit</h2></div></div></div><pre class="programlisting">
-       void
-       ao_exit(void)
-      </pre><p>
-       This terminates the current task.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53850448"></a>3. ao_sleep</h2></div></div></div><pre class="programlisting">
-       void
-       ao_sleep(__xdata void *wchan)
-      </pre><p>
-       This suspends the current task until 'wchan' is signaled
-       by ao_wakeup, or until the timeout, set by ao_alarm,
-       fires. If 'wchan' is signaled, ao_sleep returns 0, otherwise
-       it returns 1. This is the only way to switch to another task.
-      </p><p>
-       Because ao_wakeup wakes every task waiting on a particular
-       location, ao_sleep should be used in a loop that first checks
-       the desired condition, blocks in ao_sleep and then rechecks
-       until the condition is satisfied. If the location may be
-       signaled from an interrupt handler, the code will need to
-       block interrupts around the block of code. Here's a complete
-       example:
-       </p><pre class="programlisting">
-         ao_arch_block_interrupts();
-         while (!ao_radio_done)
-                 ao_sleep(&amp;ao_radio_done);
-         ao_arch_release_interrupts();
-       </pre><p>
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53854048"></a>4. ao_wakeup</h2></div></div></div><pre class="programlisting">
-       void
-       ao_wakeup(__xdata void *wchan)
-      </pre><p>
-       Wake all tasks blocked on 'wchan'. This makes them
-       available to be run again, but does not actually switch
-       to another task. Here's an example of using this:
-       </p><pre class="programlisting">
-         if (RFIF &amp; RFIF_IM_DONE) {
-                 ao_radio_done = 1;
-                 ao_wakeup(&amp;ao_radio_done);
-                 RFIF &amp;= ~RFIF_IM_DONE;
-         }
-       </pre><p>
-       Note that this need not block interrupts as the ao_sleep block
-       can only be run from normal mode, and so this sequence can
-       never be interrupted with execution of the other sequence.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53857040"></a>5. ao_alarm</h2></div></div></div><pre class="programlisting">
-       void
-       ao_alarm(uint16_t delay);
+static inline void
+ao_arch_save_stack(void);
 
-       void
-       ao_clear_alarm(void);
-      </pre><p>
-       Schedules an alarm to fire in at least 'delay' ticks. If the
-       task is asleep when the alarm fires, it will wakeup and
-       ao_sleep will return 1. ao_clear_alarm resets any pending
-       alarm so that it doesn't fire at some arbitrary point in the
-       future.
-       </p><pre class="programlisting">
-         ao_alarm(ao_packet_master_delay);
-         ao_arch_block_interrupts();
-         while (!ao_radio_dma_done)
-                 if (ao_sleep(&amp;ao_radio_dma_done) != 0)
-                         ao_radio_abort();
-         ao_arch_release_interrupts();
-         ao_clear_alarm();
-       </pre><p>
-       In this example, a timeout is set before waiting for
-       incoming radio data. If no data is received before the
-       timeout fires, ao_sleep will return 1 and then this code
-       will abort the radio receive operation.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53860224"></a>6. ao_start_scheduler</h2></div></div></div><pre class="programlisting">
-       void
-       ao_start_scheduler(void);
-      </pre><p>
-       This is called from 'main' when the system is all
-       initialized and ready to run. It will not return.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53862192"></a>7. ao_clock_init</h2></div></div></div><pre class="programlisting">
-       void
-       ao_clock_init(void);
-      </pre><p>
-       This initializes the main CPU clock and switches to it.
-      </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp53864160"></a>Chapter 5. Timer Functions</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="#idp53865664">1. ao_time</a></span></dt><dt><span class="section"><a href="#idp53867648">2. ao_delay</a></span></dt><dt><span class="section"><a href="#idp53869488">3. ao_timer_set_adc_interval</a></span></dt><dt><span class="section"><a href="#idp53871536">4. ao_timer_init</a></span></dt></dl></div><p>
-      AltOS sets up one of the CPU timers to run at 100Hz and
-      exposes this tick as the fundemental unit of time. At each
-      interrupt, AltOS increments the counter, and schedules any tasks
-      waiting for that time to pass, then fires off the sensors to
-      collect current data readings. Doing this from the ISR ensures
-      that the values are sampled at a regular rate, independent
-      of any scheduling jitter.
-    </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53865664"></a>1. ao_time</h2></div></div></div><pre class="programlisting">
-       uint16_t
-       ao_time(void)
-      </pre><p>
-       Returns the current system tick count. Note that this is
-       only a 16 bit value, and so it wraps every 655.36 seconds.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53867648"></a>2. ao_delay</h2></div></div></div><pre class="programlisting">
-       void
-       ao_delay(uint16_t ticks);
-      </pre><p>
-       Suspend the current task for at least 'ticks' clock units.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53869488"></a>3. ao_timer_set_adc_interval</h2></div></div></div><pre class="programlisting">
-       void
-       ao_timer_set_adc_interval(uint8_t interval);
-      </pre><p>
-       This sets the number of ticks between ADC samples. If set
-       to 0, no ADC samples are generated. AltOS uses this to
-       slow down the ADC sampling rate to save power.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53871536"></a>4. ao_timer_init</h2></div></div></div><pre class="programlisting">
-       void
-       ao_timer_init(void)
-      </pre><p>
-       This turns on the 100Hz tick. It is required for any of the
-       time-based functions to work. It should be called by 'main'
-       before ao_start_scheduler.
-      </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp53873728"></a>Chapter 6. AltOS Mutexes</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="#idp53875600">1. ao_mutex_get</a></span></dt><dt><span class="section"><a href="#idp53877440">2. ao_mutex_put</a></span></dt></dl></div><p>
-      AltOS provides mutexes as a basic synchronization primitive. Each
-      mutexes is simply a byte of memory which holds 0 when the mutex
-      is free or the task id of the owning task when the mutex is
-      owned. Mutex calls are checked&#8212;attempting to acquire a mutex
-      already held by the current task or releasing a mutex not held
-      by the current task will both cause a panic.
-    </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53875600"></a>1. ao_mutex_get</h2></div></div></div><pre class="programlisting">
-       void
-       ao_mutex_get(__xdata uint8_t *mutex);
-      </pre><p>
-       Acquires the specified mutex, blocking if the mutex is
-       owned by another task.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53877440"></a>2. ao_mutex_put</h2></div></div></div><pre class="programlisting">
-       void
-       ao_mutex_put(__xdata uint8_t *mutex);
-      </pre><p>
-       Releases the specified mutex, waking up all tasks waiting
-       for it.
-      </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp53879408"></a>Chapter 7. DMA engine</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="#idp53882848">1. CC1111 DMA Engine</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53883520">1.1. ao_dma_alloc</a></span></dt><dt><span class="section"><a href="#idp53885696">1.2. ao_dma_set_transfer</a></span></dt><dt><span class="section"><a href="#idp53887968">1.3. ao_dma_start</a></span></dt><dt><span class="section"><a href="#idp53889984">1.4. ao_dma_trigger</a></span></dt><dt><span class="section"><a href="#idp53891824">1.5. ao_dma_abort</a></span></dt></dl></dd><dt><span class="section"><a href="#idp53893968">2. STM32L DMA Engine</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53894640">2.1. ao_dma_alloc</a></span></dt><dt><span class="section"><a href="#idp53896480">2.2. ao_dma_set_transfer</a></span></dt><dt><span class="section"><a href="#idp53898736">2.3. ao_dma_set_isr</a></span></dt><dt><span class="section"><a href="#idp53900816">2.4. ao_dma_start</a></span></dt><dt><span class="section"><a href="#idp53903120">2.5. ao_dma_done_transfer</a></span></dt><dt><span class="section"><a href="#idp53905104">2.6. ao_dma_abort</a></span></dt></dl></dd></dl></div><p>
-      The CC1111 and STM32L both contain a useful bit of extra
-      hardware in the form of a number of programmable DMA
-      engines. They can be configured to copy data in memory, or
-      between memory and devices (or even between two devices). AltOS
-      exposes a general interface to this hardware and uses it to
-      handle both internal and external devices.
-    </p><p>
-      Because the CC1111 and STM32L DMA engines are different, the
-      interface to them is also different. As the DMA engines are
-      currently used to implement platform-specific drivers, this
-      isn't yet a problem.
-    </p><p>
-      Code using a DMA engine should allocate one at startup
-      time. There is no provision to free them, and if you run out,
-      AltOS will simply panic.
-    </p><p>
-      During operation, the DMA engine is initialized with the
-      transfer parameters. Then it is started, at which point it
-      awaits a suitable event to start copying data. When copying data
-      from hardware to memory, that trigger event is supplied by the
-      hardware device. When copying data from memory to hardware, the
-      transfer is usually initiated by software.
-    </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53882848"></a>1. CC1111 DMA Engine</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53883520"></a>1.1. ao_dma_alloc</h3></div></div></div><pre class="programlisting">
-         uint8_t
-         ao_dma_alloc(__xdata uint8_t *done)
-       </pre><p>
-         Allocate a DMA engine, returning the identifier.  'done' is
-         cleared when the DMA is started, and then receives the
-         AO_DMA_DONE bit on a successful transfer or the
-         AO_DMA_ABORTED bit if ao_dma_abort was called. Note that it
-         is possible to get both bits if the transfer was aborted
-         after it had finished.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53885696"></a>1.2. ao_dma_set_transfer</h3></div></div></div><pre class="programlisting">
-         void
-         ao_dma_set_transfer(uint8_t id,
-         void __xdata *srcaddr,
-         void __xdata *dstaddr,
-         uint16_t count,
-         uint8_t cfg0,
-         uint8_t cfg1)
-       </pre><p>
-         Initializes the specified dma engine to copy data
-         from 'srcaddr' to 'dstaddr' for 'count' units. cfg0 and
-         cfg1 are values directly out of the CC1111 documentation
-         and tell the DMA engine what the transfer unit size,
-         direction and step are.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53887968"></a>1.3. ao_dma_start</h3></div></div></div><pre class="programlisting">
-         void
-         ao_dma_start(uint8_t id);
-       </pre><p>
-         Arm the specified DMA engine and await a signal from
-         either hardware or software to start transferring data.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53889984"></a>1.4. ao_dma_trigger</h3></div></div></div><pre class="programlisting">
-         void
-         ao_dma_trigger(uint8_t id)
-       </pre><p>
-         Trigger the specified DMA engine to start copying data.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53891824"></a>1.5. ao_dma_abort</h3></div></div></div><pre class="programlisting">
-         void
-         ao_dma_abort(uint8_t id)
-       </pre><p>
-         Terminate any in-progress DMA transation, marking its
-         'done' variable with the AO_DMA_ABORTED bit.
-       </p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53893968"></a>2. STM32L DMA Engine</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53894640"></a>2.1. ao_dma_alloc</h3></div></div></div><pre class="programlisting">
-         uint8_t ao_dma_done[];
+static inline void
+ao_arch_restore_stack(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>These provide all of the support needed to switch
+between tasks.. ao_arch_save_regs must save all CPU
+registers to the current stack, including the
+interrupt enable state. ao_arch_save_stack records the
+current stack location in the current ao_task
+structure. ao_arch_restore_stack switches back to the
+saved stack, restores all registers and branches to
+the saved return address.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_arch_wait_interupt">2.1.3. ao_arch_wait_interupt</h4>
+<div class="literalblock">
+<div class="content">
+<pre>#define ao_arch_wait_interrupt()</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This stops the CPU, leaving clocks and interrupts
+enabled. When an interrupt is received, this must wake up
+and handle the interrupt. ao_arch_wait_interrupt is entered
+with interrupts disabled to ensure that there is no gap
+between determining that no task wants to run and idling the
+CPU. It must sleep the CPU, process interrupts and then
+disable interrupts again. If the CPU doesn&#8217;t have any
+reduced power mode, this must at the least allow pending
+interrupts to be processed.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_gpio_operations">2.2. GPIO operations</h3>
+<div class="paragraph">
+<p>These functions provide an abstract interface to configure and
+manipulate GPIO pins.</p>
+</div>
+<div class="sect3">
+<h4 id="_gpio_setup">2.2.1. GPIO setup</h4>
+<div class="paragraph">
+<p>These macros may be invoked at system
+initialization time to configure pins as
+needed for system operation. One tricky aspect
+is that some chips provide direct access to
+specific GPIO pins while others only provide
+access to a whole register full of pins. To
+support this, the GPIO macros provide both
+port+bit and pin arguments. Simply define the
+arguments needed for the target platform and
+leave the others undefined.</p>
+</div>
+<div class="sect4">
+<h5 id="_ao_enable_output">ao_enable_output</h5>
+<div class="literalblock">
+<div class="content">
+<pre>#define ao_enable_output(port, bit, pin, value)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Set the specified port+bit (also called 'pin')
+for output, initializing to the specified
+value. The macro must avoid driving the pin
+with the opposite value if at all possible.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_ao_enable_input">ao_enable_input</h5>
+<div class="literalblock">
+<div class="content">
+<pre>#define ao_enable_input(port, bit, mode)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Sets the specified port/bit to be an input
+pin. 'mode' is a combination of one or more of
+the following. Note that some platforms may
+not support the desired mode. In that case,
+the value will not be defined so that the
+program will fail to compile.</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>AO_EXTI_MODE_PULL_UP. Apply a pull-up to the
+pin; a disconnected pin will read as 1.</p>
+</li>
+<li>
+<p>AO_EXTI_MODE_PULL_DOWN. Apply a pull-down to
+the pin; a disconnected pin will read as 0.</p>
+</li>
+<li>
+<p>0. Don&#8217;t apply either a pull-up or
+pull-down. A disconnected pin will read an
+undetermined value.</p>
+</li>
+</ul>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_reading_and_writing_gpio_pins">2.2.2. Reading and writing GPIO pins</h4>
+<div class="paragraph">
+<p>These macros read and write individual GPIO pins.</p>
+</div>
+<div class="sect4">
+<h5 id="_ao_gpio_set">ao_gpio_set</h5>
+<div class="literalblock">
+<div class="content">
+<pre>#define ao_gpio_set(port, bit, pin, value)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Sets the specified port/bit or pin to
+the indicated value</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_ao_gpio_get">ao_gpio_get</h5>
+<div class="literalblock">
+<div class="content">
+<pre>#define ao_gpio_get(port, bit, pin)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Returns either 1 or 0 depending on
+whether the input to the pin is high
+or low.
+== Programming the 8051 with SDCC</p>
+</div>
+<div class="paragraph">
+<p>The 8051 is a primitive 8-bit processor, designed in the mists
+of time in as few transistors as possible. The architecture is
+highly irregular and includes several separate memory
+spaces. Furthermore, accessing stack variables is slow, and
+the stack itself is of limited size. While SDCC papers over
+the instruction set, it is not completely able to hide the
+memory architecture from the application designer.</p>
+</div>
+<div class="paragraph">
+<p>When built on other architectures, the various SDCC-specific
+symbols are #defined as empty strings so they don&#8217;t affect the
+compiler.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_8051_memory_spaces">2.3. 8051 memory spaces</h3>
+<div class="paragraph">
+<p>The <em>data/</em>xdata/__code memory spaces below were completely
+separate in the original 8051 design. In the cc1111, this
+isn&#8217;t true—they all live in a single unified 64kB address
+space, and so it&#8217;s possible to convert any address into a
+unique 16-bit address. SDCC doesn&#8217;t know this, and so a
+'global' address to SDCC consumes 3 bytes of memory, 1 byte as
+a tag indicating the memory space and 2 bytes of offset within
+that space. AltOS avoids these 3-byte addresses as much as
+possible; using them involves a function call per byte
+access. The result is that nearly every variable declaration
+is decorated with a memory space identifier which clutters the
+code but makes the resulting code far smaller and more
+efficient.</p>
+</div>
+<div class="sect3">
+<h4 id="_data">2.3.1. __data</h4>
+<div class="paragraph">
+<p>The 8051 can directly address these 128 bytes of
+memory. This makes them precious so they should be
+reserved for frequently addressed values. Oh, just to
+confuse things further, the 8 general registers in the
+CPU are actually stored in this memory space. There are
+magic instructions to 'bank switch' among 4 banks of
+these registers located at 0x00 - 0x1F. AltOS uses only
+the first bank at 0x00 - 0x07, leaving the other 24
+bytes available for other data.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_idata">2.3.2. __idata</h4>
+<div class="paragraph">
+<p>There are an additional 128 bytes of internal memory
+that share the same address space as __data but which
+cannot be directly addressed. The stack normally
+occupies this space and so AltOS doesn&#8217;t place any
+static storage here.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_xdata">2.3.3. __xdata</h4>
+<div class="paragraph">
+<p>This is additional general memory accessed through a
+single 16-bit address register. The CC1111F32 has 32kB
+of memory available here. Most program data should live
+in this memory space.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_pdata">2.3.4. __pdata</h4>
+<div class="paragraph">
+<p>This is an alias for the first 256 bytes of __xdata
+memory, but uses a shorter addressing mode with
+single global 8-bit value for the high 8 bits of the
+address and any of several 8-bit registers for the low 8
+bits. AltOS uses a few bits of this memory, it should
+probably use more.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_code">2.3.5. __code</h4>
+<div class="paragraph">
+<p>All executable code must live in this address space, but
+you can stick read-only data here too. It is addressed
+using the 16-bit address register and special 'code'
+access opcodes. Anything read-only should live in this space.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_bit">2.3.6. __bit</h4>
+<div class="paragraph">
+<p>The 8051 has 128 bits of bit-addressible memory that
+lives in the <em>data segment from 0x20 through
+0x2f. Special instructions access these bits
+in a single atomic operation. This isn&#8217;t so much a
+separate address space as a special addressing mode for
+a few bytes in the </em>data segment.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_sfr_sfr16_sfr32_sbit">2.3.7. <em>sfr, </em>sfr16, <em>sfr32, </em>sbit</h4>
+<div class="paragraph">
+<p>Access to physical registers in the device use this mode
+which declares the variable name, its type and the
+address it lives at. No memory is allocated for these
+variables.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_function_calls_on_the_8051">2.4. Function calls on the 8051</h3>
+<div class="paragraph">
+<p>Because stack addressing is expensive, and stack space
+limited, the default function call declaration in SDCC
+allocates all parameters and local variables in static global
+memory. Just like fortran. This makes these functions
+non-reentrant, and also consume space for parameters and
+locals even when they are not running. The benefit is smaller
+code and faster execution.</p>
+</div>
+<div class="sect3">
+<h4 id="_reentrant_functions">2.4.1. __reentrant functions</h4>
+<div class="paragraph">
+<p>All functions which are re-entrant, either due to recursion
+or due to a potential context switch while executing, should
+be marked as __reentrant so that their parameters and local
+variables get allocated on the stack. This ensures that
+these values are not overwritten by another invocation of
+the function.</p>
+</div>
+<div class="paragraph">
+<p>Functions which use significant amounts of space for
+arguments and/or local variables and which are not often
+invoked can also be marked as __reentrant. The resulting
+code will be larger, but the savings in memory are
+frequently worthwhile.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_non_reentrant_functions">2.4.2. Non __reentrant functions</h4>
+<div class="paragraph">
+<p>All parameters and locals in non-reentrant functions can
+have data space decoration so that they are allocated in
+<em>xdata, </em>pdata or <em>data space as desired. This can avoid
+consuming </em>data space for infrequently used variables in
+frequently used functions.</p>
+</div>
+<div class="paragraph">
+<p>All library functions called by SDCC, including functions
+for multiplying and dividing large data types, are
+non-reentrant. Because of this, interrupt handlers must not
+invoke any library functions, including the multiply and
+divide code.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_interrupt_functions">2.4.3. __interrupt functions</h4>
+<div class="paragraph">
+<p>Interrupt functions are declared with with an __interrupt
+decoration that includes the interrupt number. SDCC saves
+and restores all of the registers in these functions and
+uses the 'reti' instruction at the end so that they operate
+as stand-alone interrupt handlers. Interrupt functions may
+call the ao_wakeup function to wake AltOS tasks.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_critical_functions_and_statements">2.4.4. __critical functions and statements</h4>
+<div class="paragraph">
+<p>SDCC has built-in support for suspending interrupts during
+critical code. Functions marked as <em>critical will have
+interrupts suspended for the whole period of
+execution. Individual statements may also be marked as
+</em>critical which blocks interrupts during the execution of
+that statement. Keeping critical sections as short as
+possible is key to ensuring that interrupts are handled as
+quickly as possible. AltOS doesn&#8217;t use this form in shared
+code as other compilers wouldn&#8217;t know what to do. Use
+ao_arch_block_interrupts and ao_arch_release_interrupts instead.</p>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_task_functions">3. Task functions</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>This chapter documents how to create, destroy and schedule
+AltOS tasks.</p>
+</div>
+<div class="sect2">
+<h3 id="_ao_add_task">3.1. ao_add_task</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_add_task(__xdata struct ao_task * task,
+           void (*start)(void),
+           __code char *name);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This initializes the statically allocated task structure,
+assigns a name to it (not used for anything but the task
+display), and the start address. It does not switch to the
+new task. 'start' must not ever return; there is no place
+to return to.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_exit">3.2. ao_exit</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_exit(void)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This terminates the current task.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_sleep">3.3. ao_sleep</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_sleep(__xdata void *wchan)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This suspends the current task until 'wchan' is signaled
+by ao_wakeup, or until the timeout, set by ao_alarm,
+fires. If 'wchan' is signaled, ao_sleep returns 0, otherwise
+it returns 1. This is the only way to switch to another task.</p>
+</div>
+<div class="paragraph">
+<p>Because ao_wakeup wakes every task waiting on a particular
+location, ao_sleep should be used in a loop that first checks
+the desired condition, blocks in ao_sleep and then rechecks
+until the condition is satisfied. If the location may be
+signaled from an interrupt handler, the code will need to
+block interrupts around the block of code. Here&#8217;s a complete
+example:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>ao_arch_block_interrupts();
+while (!ao_radio_done)
+       ao_sleep(&amp;amp;ao_radio_done);
+ao_arch_release_interrupts();</pre>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_wakeup">3.4. ao_wakeup</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_wakeup(__xdata void *wchan)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Wake all tasks blocked on 'wchan'. This makes them
+available to be run again, but does not actually switch
+to another task. Here&#8217;s an example of using this:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>if (RFIF &amp;amp; RFIF_IM_DONE) {
+       ao_radio_done = 1;
+       ao_wakeup(&amp;amp;ao_radio_done);
+       RFIF &amp;amp;= ~RFIF_IM_DONE;
+}</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Note that this need not block interrupts as the
+ao_sleep block can only be run from normal mode, and
+so this sequence can never be interrupted with
+execution of the other sequence.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_alarm">3.5. ao_alarm</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_alarm(uint16_t delay);
 
-         void
-         ao_dma_alloc(uint8_t index);
-       </pre><p>
-         Reserve a DMA engine for exclusive use by one
-         driver.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53896480"></a>2.2. ao_dma_set_transfer</h3></div></div></div><pre class="programlisting">
-         void
-         ao_dma_set_transfer(uint8_t id,
-         void *peripheral,
-         void *memory,
-         uint16_t count,
-         uint32_t ccr);
-       </pre><p>
-         Initializes the specified dma engine to copy data between
-         'peripheral' and 'memory' for 'count' units. 'ccr' is a
-         value directly out of the STM32L documentation and tells the
-         DMA engine what the transfer unit size, direction and step
-         are.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53898736"></a>2.3. ao_dma_set_isr</h3></div></div></div><pre class="programlisting">
-         void
-         ao_dma_set_isr(uint8_t index, void (*isr)(int))
-       </pre><p>
-         This sets a function to be called when the DMA transfer
-         completes in lieu of setting the ao_dma_done bits. Use this
-         when some work needs to be done when the DMA finishes that
-         cannot wait until user space resumes.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53900816"></a>2.4. ao_dma_start</h3></div></div></div><pre class="programlisting">
-         void
-         ao_dma_start(uint8_t id);
-       </pre><p>
-         Arm the specified DMA engine and await a signal from either
-         hardware or software to start transferring data.
-         'ao_dma_done[index]' is cleared when the DMA is started, and
-         then receives the AO_DMA_DONE bit on a successful transfer
-         or the AO_DMA_ABORTED bit if ao_dma_abort was called. Note
-         that it is possible to get both bits if the transfer was
-         aborted after it had finished.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53903120"></a>2.5. ao_dma_done_transfer</h3></div></div></div><pre class="programlisting">
-         void
-         ao_dma_done_transfer(uint8_t id);
-       </pre><p>
-         Signals that a specific DMA engine is done being used. This
-         allows multiple drivers to use the same DMA engine safely.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53905104"></a>2.6. ao_dma_abort</h3></div></div></div><pre class="programlisting">
-         void
-         ao_dma_abort(uint8_t id)
-       </pre><p>
-         Terminate any in-progress DMA transation, marking its
-         'done' variable with the AO_DMA_ABORTED bit.
-       </p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp53907376"></a>Chapter 8. Stdio interface</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="#idp53908880">1. putchar</a></span></dt><dt><span class="section"><a href="#idp53910768">2. getchar</a></span></dt><dt><span class="section"><a href="#idp53912864">3. flush</a></span></dt><dt><span class="section"><a href="#idp53914896">4. ao_add_stdio</a></span></dt></dl></div><p>
-      AltOS offers a stdio interface over USB, serial and the RF
-      packet link. This provides for control of the device localy or
-      remotely. This is hooked up to the stdio functions by providing
-      the standard putchar/getchar/flush functions. These
-      automatically multiplex the available communication channels;
-      output is always delivered to the channel which provided the
-      most recent input.
-    </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53908880"></a>1. putchar</h2></div></div></div><pre class="programlisting">
-       void
-       putchar(char c)
-      </pre><p>
-       Delivers a single character to the current console
-       device.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53910768"></a>2. getchar</h2></div></div></div><pre class="programlisting">
-       char
-       getchar(void)
-      </pre><p>
-       Reads a single character from any of the available
-       console devices. The current console device is set to
-       that which delivered this character. This blocks until
-       a character is available.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53912864"></a>3. flush</h2></div></div></div><pre class="programlisting">
-       void
-       flush(void)
-      </pre><p>
-       Flushes the current console device output buffer. Any
-       pending characters will be delivered to the target device.
-      xo         </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53914896"></a>4. ao_add_stdio</h2></div></div></div><pre class="programlisting">
-       void
-       ao_add_stdio(char (*pollchar)(void),
-                          void (*putchar)(char),
-                          void (*flush)(void))
-      </pre><p>
-       This adds another console device to the available
-       list.
-      </p><p>
-       'pollchar' returns either an available character or
-       AO_READ_AGAIN if none is available. Significantly, it does
-       not block. The device driver must set 'ao_stdin_ready' to
-       1 and call ao_wakeup(&amp;ao_stdin_ready) when it receives
-       input to tell getchar that more data is available, at
-       which point 'pollchar' will be called again.
-      </p><p>
-       'putchar' queues a character for output, flushing if the output buffer is
-       full. It may block in this case.
-      </p><p>
-       'flush' forces the output buffer to be flushed. It may
-       block until the buffer is delivered, but it is not
-       required to do so.
-      </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp53918848"></a>Chapter 9. Command line interface</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="#idp53920256">1. ao_cmd_register</a></span></dt><dt><span class="section"><a href="#idp53928816">2. ao_cmd_lex</a></span></dt><dt><span class="section"><a href="#idp53930896">3. ao_cmd_put16</a></span></dt><dt><span class="section"><a href="#idp53932736">4. ao_cmd_put8</a></span></dt><dt><span class="section"><a href="#idp53934624">5. ao_cmd_white</a></span></dt><dt><span class="section"><a href="#idp53936688">6. ao_cmd_hex</a></span></dt><dt><span class="section"><a href="#idp53938736">7. ao_cmd_decimal</a></span></dt><dt><span class="section"><a href="#idp53940832">8. ao_match_word</a></span></dt><dt><span class="section"><a href="#idp53942912">9. ao_cmd_init</a></span></dt></dl></div><p>
-      AltOS includes a simple command line parser which is hooked up
-      to the stdio interfaces permitting remote control of the device
-      over USB, serial or the RF link as desired. Each command uses a
-      single character to invoke it, the remaining characters on the
-      line are available as parameters to the command.
-    </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53920256"></a>1. ao_cmd_register</h2></div></div></div><pre class="programlisting">
-       void
-       ao_cmd_register(__code struct ao_cmds *cmds)
-      </pre><p>
-       This registers a set of commands with the command
-       parser. There is a fixed limit on the number of command
-       sets, the system will panic if too many are registered.
-       Each command is defined by a struct ao_cmds entry:
-       </p><pre class="programlisting">
-         struct ao_cmds {
-                 char          cmd;
-                 void          (*func)(void);
-                 const char    *help;
-         };
-       </pre><p>
-       'cmd' is the character naming the command. 'func' is the
-       function to invoke and 'help' is a string displayed by the
-       '?' command. Syntax errors found while executing 'func'
-       should be indicated by modifying the global ao_cmd_status
-       variable with one of the following values:
-       </p><div class="variablelist"><dl class="variablelist"><dt><span class="term">ao_cmd_success</span></dt><dd><p>
-               The command was parsed successfully. There is no
-               need to assign this value, it is the default.
-             </p></dd><dt><span class="term">ao_cmd_lex_error</span></dt><dd><p>
-               A token in the line was invalid, such as a number
-               containing invalid characters. The low-level
-               lexing functions already assign this value as needed.
-             </p></dd><dt><span class="term">ao_syntax_error</span></dt><dd><p>
-               The command line is invalid for some reason other
-               than invalid tokens.
-             </p></dd></dl></div><p>
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53928816"></a>2. ao_cmd_lex</h2></div></div></div><pre class="programlisting">
-       void
-       ao_cmd_lex(void);
-      </pre><p>
-       This gets the next character out of the command line
-       buffer and sticks it into ao_cmd_lex_c. At the end of the
-       line, ao_cmd_lex_c will get a newline ('\n') character.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53930896"></a>3. ao_cmd_put16</h2></div></div></div><pre class="programlisting">
-       void
-       ao_cmd_put16(uint16_t v);
-      </pre><p>
-       Writes 'v' as four hexadecimal characters.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53932736"></a>4. ao_cmd_put8</h2></div></div></div><pre class="programlisting">
-       void
-       ao_cmd_put8(uint8_t v);
-      </pre><p>
-       Writes 'v' as two hexadecimal characters.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53934624"></a>5. ao_cmd_white</h2></div></div></div><pre class="programlisting">
-       void
-       ao_cmd_white(void)
-      </pre><p>
-       This skips whitespace by calling ao_cmd_lex while
-       ao_cmd_lex_c is either a space or tab. It does not skip
-       any characters if ao_cmd_lex_c already non-white.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53936688"></a>6. ao_cmd_hex</h2></div></div></div><pre class="programlisting">
-       void
-       ao_cmd_hex(void)
-      </pre><p>
-       This reads a 16-bit hexadecimal value from the command
-       line with optional leading whitespace. The resulting value
-       is stored in ao_cmd_lex_i;
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53938736"></a>7. ao_cmd_decimal</h2></div></div></div><pre class="programlisting">
-       void
-       ao_cmd_decimal(void)
-      </pre><p>
-       This reads a 32-bit decimal value from the command
-       line with optional leading whitespace. The resulting value
-       is stored in ao_cmd_lex_u32 and the low 16 bits are stored
-       in ao_cmd_lex_i;
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53940832"></a>8. ao_match_word</h2></div></div></div><pre class="programlisting">
-       uint8_t
-       ao_match_word(__code char *word)
-      </pre><p>
-       This checks to make sure that 'word' occurs on the command
-       line. It does not skip leading white space. If 'word' is
-       found, then 1 is returned. Otherwise, ao_cmd_status is set to
-       ao_cmd_syntax_error and 0 is returned.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53942912"></a>9. ao_cmd_init</h2></div></div></div><pre class="programlisting">
-       void
-       ao_cmd_init(void
-      </pre><p>
-       Initializes the command system, setting up the built-in
-       commands and adding a task to run the command processing
-       loop. It should be called by 'main' before ao_start_scheduler.
-      </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp53945136"></a>Chapter 10. USB target device</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="#idp53947360">1. ao_usb_flush</a></span></dt><dt><span class="section"><a href="#idp53949504">2. ao_usb_putchar</a></span></dt><dt><span class="section"><a href="#idp53951680">3. ao_usb_pollchar</a></span></dt><dt><span class="section"><a href="#idp53953824">4. ao_usb_getchar</a></span></dt><dt><span class="section"><a href="#idp53955840">5. ao_usb_disable</a></span></dt><dt><span class="section"><a href="#idp53958672">6. ao_usb_enable</a></span></dt><dt><span class="section"><a href="#idp53960784">7. ao_usb_init</a></span></dt></dl></div><p>
-      AltOS contains a full-speed USB target device driver. It can be
-      programmed to offer any kind of USB target, but to simplify
-      interactions with a variety of operating systems, AltOS provides
-      only a single target device profile, that of a USB modem which
-      has native drivers for Linux, Windows and Mac OS X. It would be
-      easy to change the code to provide an alternate target device if
-      necessary.
-    </p><p>
-      To the rest of the system, the USB device looks like a simple
-      two-way byte stream. It can be hooked into the command line
-      interface if desired, offering control of the device over the
-      USB link. Alternatively, the functions can be accessed directly
-      to provide for USB-specific I/O.
-    </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53947360"></a>1. ao_usb_flush</h2></div></div></div><pre class="programlisting">
-       void
-       ao_usb_flush(void);
-      </pre><p>
-       Flushes any pending USB output. This queues an 'IN' packet
-       to be delivered to the USB host if there is pending data,
-       or if the last IN packet was full to indicate to the host
-       that there isn't any more pending data available.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53949504"></a>2. ao_usb_putchar</h2></div></div></div><pre class="programlisting">
-       void
-       ao_usb_putchar(char c);
-      </pre><p>
-       If there is a pending 'IN' packet awaiting delivery to the
-       host, this blocks until that has been fetched. Then, this
-       adds a byte to the pending IN packet for delivery to the
-       USB host. If the USB packet is full, this queues the 'IN'
-       packet for delivery.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53951680"></a>3. ao_usb_pollchar</h2></div></div></div><pre class="programlisting">
-       char
-       ao_usb_pollchar(void);
-      </pre><p>
-       If there are no characters remaining in the last 'OUT'
-       packet received, this returns AO_READ_AGAIN. Otherwise, it
-       returns the next character, reporting to the host that it
-       is ready for more data when the last character is gone.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53953824"></a>4. ao_usb_getchar</h2></div></div></div><pre class="programlisting">
-       char
-       ao_usb_getchar(void);
-      </pre><p>
-       This uses ao_pollchar to receive the next character,
-       blocking while ao_pollchar returns AO_READ_AGAIN.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53955840"></a>5. ao_usb_disable</h2></div></div></div><pre class="programlisting">
-       void
-       ao_usb_disable(void);
-      </pre><p>
-       This turns off the USB controller. It will no longer
-       respond to host requests, nor return characters. Calling
-       any of the i/o routines while the USB device is disabled
-       is undefined, and likely to break things. Disabling the
-       USB device when not needed saves power.
-      </p><p>
-       Note that neither TeleDongle nor TeleMetrum are able to
-       signal to the USB host that they have disconnected, so
-       after disabling the USB device, it's likely that the cable
-       will need to be disconnected and reconnected before it
-       will work again.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53958672"></a>6. ao_usb_enable</h2></div></div></div><pre class="programlisting">
-       void
-       ao_usb_enable(void);
-      </pre><p>
-       This turns the USB controller on again after it has been
-       disabled. See the note above about needing to physically
-       remove and re-insert the cable to get the host to
-       re-initialize the USB link.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53960784"></a>7. ao_usb_init</h2></div></div></div><pre class="programlisting">
-       void
-       ao_usb_init(void);
-      </pre><p>
-       This turns the USB controller on, adds a task to handle
-       the control end point and adds the usb I/O functions to
-       the stdio system. Call this from main before
-       ao_start_scheduler.
-      </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp53963008"></a>Chapter 11. Serial peripherals</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="#idp53965024">1. ao_serial_getchar</a></span></dt><dt><span class="section"><a href="#idp53967056">2. ao_serial_putchar</a></span></dt><dt><span class="section"><a href="#idp53969024">3. ao_serial_drain</a></span></dt><dt><span class="section"><a href="#idp53971024">4. ao_serial_set_speed</a></span></dt><dt><span class="section"><a href="#idp53973056">5. ao_serial_init</a></span></dt></dl></div><p>
-      The CC1111 provides two USART peripherals. AltOS uses one for
-      asynch serial data, generally to communicate with a GPS device,
-      and the other for a SPI bus. The UART is configured to operate
-      in 8-bits, no parity, 1 stop bit framing. The default
-      configuration has clock settings for 4800, 9600 and 57600 baud
-      operation. Additional speeds can be added by computing
-      appropriate clock values.
-    </p><p>
-      To prevent loss of data, AltOS provides receive and transmit
-      fifos of 32 characters each.
-    </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53965024"></a>1. ao_serial_getchar</h2></div></div></div><pre class="programlisting">
-       char
-       ao_serial_getchar(void);
-      </pre><p>
-       Returns the next character from the receive fifo, blocking
-       until a character is received if the fifo is empty.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53967056"></a>2. ao_serial_putchar</h2></div></div></div><pre class="programlisting">
-       void
-       ao_serial_putchar(char c);
-      </pre><p>
-       Adds a character to the transmit fifo, blocking if the
-       fifo is full. Starts transmitting characters.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53969024"></a>3. ao_serial_drain</h2></div></div></div><pre class="programlisting">
-       void
-       ao_serial_drain(void);
-      </pre><p>
-       Blocks until the transmit fifo is empty. Used internally
-       when changing serial speeds.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53971024"></a>4. ao_serial_set_speed</h2></div></div></div><pre class="programlisting">
-       void
-       ao_serial_set_speed(uint8_t speed);
-      </pre><p>
-       Changes the serial baud rate to one of
-       AO_SERIAL_SPEED_4800, AO_SERIAL_SPEED_9600 or
-       AO_SERIAL_SPEED_57600. This first flushes the transmit
-       fifo using ao_serial_drain.
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53973056"></a>5. ao_serial_init</h2></div></div></div><pre class="programlisting">
-       void
-       ao_serial_init(void)
-      </pre><p>
-       Initializes the serial peripheral. Call this from 'main'
-       before jumping to ao_start_scheduler. The default speed
-       setting is AO_SERIAL_SPEED_4800.
-      </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idp53975248"></a>Chapter 12. CC1111 Radio peripheral</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="#idp53975920">1. Radio Introduction</a></span></dt><dt><span class="section"><a href="#idp53983040">2. ao_radio_set_telemetry</a></span></dt><dt><span class="section"><a href="#idp53985200">3. ao_radio_set_packet</a></span></dt><dt><span class="section"><a href="#idp53987360">4. ao_radio_set_rdf</a></span></dt><dt><span class="section"><a href="#idp53989600">5. ao_radio_idle</a></span></dt><dt><span class="section"><a href="#idp53991648">6. ao_radio_get</a></span></dt><dt><span class="section"><a href="#idp53993680">7. ao_radio_put</a></span></dt><dt><span class="section"><a href="#idp53995616">8. ao_radio_abort</a></span></dt><dt><span class="section"><a href="#idp53997680">9. Radio Telemetry</a></span></dt><dd><dl><dt><span class="section"><a href="#idp53998976">9.1. ao_radio_send</a></span></dt><dt><span class="section"><a href="#idp54001120">9.2. ao_radio_recv</a></span></dt></dl></dd><dt><span class="section"><a href="#idp54003504">10. Radio Direction Finding</a></span></dt><dd><dl><dt><span class="section"><a href="#idp54004656">10.1. ao_radio_rdf</a></span></dt></dl></dd><dt><span class="section"><a href="#idp54006800">11. Radio Packet Mode</a></span></dt><dd><dl><dt><span class="section"><a href="#idp54008112">11.1. ao_packet_putchar</a></span></dt><dt><span class="section"><a href="#idp54010288">11.2. ao_packet_pollchar</a></span></dt><dt><span class="section"><a href="#idp54012368">11.3. ao_packet_slave_start</a></span></dt><dt><span class="section"><a href="#idp54014320">11.4. ao_packet_slave_stop</a></span></dt><dt><span class="section"><a href="#idp54016160">11.5. ao_packet_slave_init</a></span></dt><dt><span class="section"><a href="#idp54018192">11.6. ao_packet_master_init</a></span></dt></dl></dd></dl></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53975920"></a>1. Radio Introduction</h2></div></div></div><p>
-       The CC1111 radio transceiver sends and receives digital packets
-       with forward error correction and detection. The AltOS driver is
-       fairly specific to the needs of the TeleMetrum and TeleDongle
-       devices, using it for other tasks may require customization of
-       the driver itself. There are three basic modes of operation:
-       </p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
-             Telemetry mode. In this mode, TeleMetrum transmits telemetry
-             frames at a fixed rate. The frames are of fixed size. This
-             is strictly a one-way communication from TeleMetrum to
-             TeleDongle.
-           </p></li><li class="listitem"><p>
-             Packet mode. In this mode, the radio is used to create a
-             reliable duplex byte stream between TeleDongle and
-             TeleMetrum. This is an asymmetrical protocol with
-             TeleMetrum only transmitting in response to a packet sent
-             from TeleDongle. Thus getting data from TeleMetrum to
-             TeleDongle requires polling. The polling rate is adaptive,
-             when no data has been received for a while, the rate slows
-             down. The packets are checked at both ends and invalid
-             data are ignored.
-           </p><p>
-             On the TeleMetrum side, the packet link is hooked into the
-             stdio mechanism, providing an alternate data path for the
-             command processor. It is enabled when the unit boots up in
-             'idle' mode.
-           </p><p>
-             On the TeleDongle side, the packet link is enabled with a
-             command; data from the stdio package is forwarded over the
-             packet link providing a connection from the USB command
-             stream to the remote TeleMetrum device.
-           </p></li><li class="listitem"><p>
-             Radio Direction Finding mode. In this mode, TeleMetrum
-             constructs a special packet that sounds like an audio tone
-             when received by a conventional narrow-band FM
-             receiver. This is designed to provide a beacon to track
-             the device when other location mechanisms fail.
-           </p></li></ol></div><p>
-      </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53983040"></a>2. ao_radio_set_telemetry</h2></div></div></div><pre class="programlisting">
-         void
-         ao_radio_set_telemetry(void);
-       </pre><p>
-         Configures the radio to send or receive telemetry
-         packets. This includes packet length, modulation scheme and
-         other RF parameters. It does not include the base frequency
-         or channel though. Those are set at the time of transmission
-         or reception, in case the values are changed by the user.
-       </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53985200"></a>3. ao_radio_set_packet</h2></div></div></div><pre class="programlisting">
-         void
-         ao_radio_set_packet(void);
-       </pre><p>
-         Configures the radio to send or receive packet data.  This
-         includes packet length, modulation scheme and other RF
-         parameters. It does not include the base frequency or
-         channel though. Those are set at the time of transmission or
-         reception, in case the values are changed by the user.
-       </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53987360"></a>4. ao_radio_set_rdf</h2></div></div></div><pre class="programlisting">
-         void
-         ao_radio_set_rdf(void);
-       </pre><p>
-         Configures the radio to send RDF 'packets'. An RDF 'packet'
-         is a sequence of hex 0x55 bytes sent at a base bit rate of
-         2kbps using a 5kHz deviation. All of the error correction
-         and data whitening logic is turned off so that the resulting
-         modulation is received as a 1kHz tone by a conventional 70cm
-         FM audio receiver.
-       </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53989600"></a>5. ao_radio_idle</h2></div></div></div><pre class="programlisting">
-         void
-         ao_radio_idle(void);
-       </pre><p>
-         Sets the radio device to idle mode, waiting until it reaches
-         that state. This will terminate any in-progress transmit or
-         receive operation.
-       </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53991648"></a>6. ao_radio_get</h2></div></div></div><pre class="programlisting">
-         void
-         ao_radio_get(void);
-       </pre><p>
-         Acquires the radio mutex and then configures the radio
-         frequency using the global radio calibration and channel
-         values.
-       </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53993680"></a>7. ao_radio_put</h2></div></div></div><pre class="programlisting">
-         void
-         ao_radio_put(void);
-       </pre><p>
-         Releases the radio mutex.
-       </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53995616"></a>8. ao_radio_abort</h2></div></div></div><pre class="programlisting">
-         void
-         ao_radio_abort(void);
-       </pre><p>
-         Aborts any transmission or reception process by aborting the
-         associated DMA object and calling ao_radio_idle to terminate
-         the radio operation.
-       </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp53997680"></a>9. Radio Telemetry</h2></div></div></div><p>
-       In telemetry mode, you can send or receive a telemetry
-       packet. The data from receiving a packet also includes the RSSI
-       and status values supplied by the receiver. These are added
-       after the telemetry data.
-      </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp53998976"></a>9.1. ao_radio_send</h3></div></div></div><pre class="programlisting">
-         void
-         ao_radio_send(__xdata struct ao_telemetry *telemetry);
-       </pre><p>
-         This sends the specific telemetry packet, waiting for the
-         transmission to complete. The radio must have been set to
-         telemetry mode. This function calls ao_radio_get() before
-         sending, and ao_radio_put() afterwards, to correctly
-         serialize access to the radio device.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp54001120"></a>9.2. ao_radio_recv</h3></div></div></div><pre class="programlisting">
-         void
-         ao_radio_recv(__xdata struct ao_radio_recv *radio);
-       </pre><p>
-         This blocks waiting for a telemetry packet to be received.
-         The radio must have been set to telemetry mode. This
-         function calls ao_radio_get() before receiving, and
-         ao_radio_put() afterwards, to correctly serialize access
-         to the radio device. This returns non-zero if a packet was
-         received, or zero if the operation was aborted (from some
-         other task calling ao_radio_abort()).
-       </p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp54003504"></a>10. Radio Direction Finding</h2></div></div></div><p>
-       In radio direction finding mode, there's just one function to
-       use
-      </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp54004656"></a>10.1. ao_radio_rdf</h3></div></div></div><pre class="programlisting">
-         void
-         ao_radio_rdf(int ms);
-       </pre><p>
-         This sends an RDF packet lasting for the specified amount
-         of time. The maximum length is 1020 ms.
-       </p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp54006800"></a>11. Radio Packet Mode</h2></div></div></div><p>
-       Packet mode is asymmetrical and is configured at compile time
-       for either master or slave mode (but not both). The basic I/O
-       functions look the same at both ends, but the internals are
-       different, along with the initialization steps.
-      </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp54008112"></a>11.1. ao_packet_putchar</h3></div></div></div><pre class="programlisting">
-         void
-         ao_packet_putchar(char c);
-       </pre><p>
-         If the output queue is full, this first blocks waiting for
-         that data to be delivered. Then, queues a character for
-         packet transmission. On the master side, this will
-         transmit a packet if the output buffer is full. On the
-         slave side, any pending data will be sent the next time
-         the master polls for data.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp54010288"></a>11.2. ao_packet_pollchar</h3></div></div></div><pre class="programlisting">
-         char
-         ao_packet_pollchar(void);
-       </pre><p>
-         This returns a pending input character if available,
-         otherwise returns AO_READ_AGAIN. On the master side, if
-         this empties the buffer, it triggers a poll for more data.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp54012368"></a>11.3. ao_packet_slave_start</h3></div></div></div><pre class="programlisting">
-         void
-         ao_packet_slave_start(void);
-       </pre><p>
-         This is available only on the slave side and starts a task
-         to listen for packet data.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp54014320"></a>11.4. ao_packet_slave_stop</h3></div></div></div><pre class="programlisting">
-         void
-         ao_packet_slave_stop(void);
-       </pre><p>
-         Disables the packet slave task, stopping the radio receiver.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp54016160"></a>11.5. ao_packet_slave_init</h3></div></div></div><pre class="programlisting">
-         void
-         ao_packet_slave_init(void);
-       </pre><p>
-         Adds the packet stdio functions to the stdio package so
-         that when packet slave mode is enabled, characters will
-         get send and received through the stdio functions.
-       </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp54018192"></a>11.6. ao_packet_master_init</h3></div></div></div><pre class="programlisting">
-         void
-         ao_packet_master_init(void);
-       </pre><p>
-         Adds the 'p' packet forward command to start packet mode.
-       </p></div></div></div></div></body></html>
+void
+ao_clear_alarm(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Schedules an alarm to fire in at least 'delay'
+ticks. If the task is asleep when the alarm fires, it
+will wakeup and ao_sleep will return 1. ao_clear_alarm
+resets any pending alarm so that it doesn&#8217;t fire at
+some arbitrary point in the future.</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>ao_alarm(ao_packet_master_delay);
+ao_arch_block_interrupts();
+while (!ao_radio_dma_done)
+if (ao_sleep(&amp;amp;ao_radio_dma_done) != 0)
+ao_radio_abort();
+ao_arch_release_interrupts();
+ao_clear_alarm();</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>In this example, a timeout is set before waiting for
+incoming radio data. If no data is received before the
+timeout fires, ao_sleep will return 1 and then this
+code will abort the radio receive operation.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_start_scheduler">3.6. ao_start_scheduler</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_start_scheduler(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This is called from 'main' when the system is all
+initialized and ready to run. It will not return.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_clock_init">3.7. ao_clock_init</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_clock_init(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This initializes the main CPU clock and switches to it.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_timer_functions">4. Timer Functions</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>AltOS sets up one of the CPU timers to run at 100Hz and
+exposes this tick as the fundemental unit of time. At each
+interrupt, AltOS increments the counter, and schedules any tasks
+waiting for that time to pass, then fires off the sensors to
+collect current data readings. Doing this from the ISR ensures
+that the values are sampled at a regular rate, independent
+of any scheduling jitter.</p>
+</div>
+<div class="sect2">
+<h3 id="_ao_time">4.1. ao_time</h3>
+<div class="literalblock">
+<div class="content">
+<pre>uint16_t
+ao_time(void)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Returns the current system tick count. Note that this is
+only a 16 bit value, and so it wraps every 655.36 seconds.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_delay">4.2. ao_delay</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_delay(uint16_t ticks);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Suspend the current task for at least 'ticks' clock units.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_timer_set_adc_interval">4.3. ao_timer_set_adc_interval</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_timer_set_adc_interval(uint8_t interval);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This sets the number of ticks between ADC samples. If set
+to 0, no ADC samples are generated. AltOS uses this to
+slow down the ADC sampling rate to save power.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_timer_init">4.4. ao_timer_init</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_timer_init(void)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This turns on the 100Hz tick. It is required for any of the
+time-based functions to work. It should be called by 'main'
+before ao_start_scheduler.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_altos_mutexes">5. AltOS Mutexes</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>AltOS provides mutexes as a basic synchronization primitive. Each
+mutexes is simply a byte of memory which holds 0 when the mutex
+is free or the task id of the owning task when the mutex is
+owned. Mutex calls are checked—attempting to acquire a mutex
+already held by the current task or releasing a mutex not held
+by the current task will both cause a panic.</p>
+</div>
+<div class="sect2">
+<h3 id="_ao_mutex_get">5.1. ao_mutex_get</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_mutex_get(__xdata uint8_t *mutex);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Acquires the specified mutex, blocking if the mutex is
+owned by another task.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_mutex_put">5.2. ao_mutex_put</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_mutex_put(__xdata uint8_t *mutex);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Releases the specified mutex, waking up all tasks waiting
+for it.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_dma_engine">6. DMA engine</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>The CC1111 and STM32L both contain a useful bit of extra
+hardware in the form of a number of programmable DMA
+engines. They can be configured to copy data in memory, or
+between memory and devices (or even between two devices). AltOS
+exposes a general interface to this hardware and uses it to
+handle both internal and external devices.</p>
+</div>
+<div class="paragraph">
+<p>Because the CC1111 and STM32L DMA engines are different, the
+interface to them is also different. As the DMA engines are
+currently used to implement platform-specific drivers, this
+isn&#8217;t yet a problem.</p>
+</div>
+<div class="paragraph">
+<p>Code using a DMA engine should allocate one at startup
+time. There is no provision to free them, and if you run out,
+AltOS will simply panic.</p>
+</div>
+<div class="paragraph">
+<p>During operation, the DMA engine is initialized with the
+transfer parameters. Then it is started, at which point it
+awaits a suitable event to start copying data. When copying data
+from hardware to memory, that trigger event is supplied by the
+hardware device. When copying data from memory to hardware, the
+transfer is usually initiated by software.</p>
+</div>
+<div class="sect2">
+<h3 id="_cc1111_dma_engine">6.1. CC1111 DMA Engine</h3>
+<div class="sect3">
+<h4 id="_ao_dma_alloc">6.1.1. ao_dma_alloc</h4>
+<div class="literalblock">
+<div class="content">
+<pre>uint8_t
+ao_dma_alloc(__xdata uint8_t *done)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Allocate a DMA engine, returning the
+identifier.  'done' is cleared when the DMA is
+started, and then receives the AO_DMA_DONE bit
+on a successful transfer or the AO_DMA_ABORTED
+bit if ao_dma_abort was called. Note that it
+is possible to get both bits if the transfer
+was aborted after it had finished.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_dma_set_transfer">6.1.2. ao_dma_set_transfer</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_dma_set_transfer(uint8_t id,
+void __xdata *srcaddr,
+void __xdata *dstaddr,
+uint16_t count,
+uint8_t cfg0,
+uint8_t cfg1)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Initializes the specified dma engine to copy
+data from 'srcaddr' to 'dstaddr' for 'count'
+units. cfg0 and cfg1 are values directly out
+of the CC1111 documentation and tell the DMA
+engine what the transfer unit size, direction
+and step are.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_dma_start">6.1.3. ao_dma_start</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_dma_start(uint8_t id);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Arm the specified DMA engine and await a
+signal from either hardware or software to
+start transferring data.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_dma_trigger">6.1.4. ao_dma_trigger</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_dma_trigger(uint8_t id)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Trigger the specified DMA engine to start
+copying data.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_dma_abort">6.1.5. ao_dma_abort</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_dma_abort(uint8_t id)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Terminate any in-progress DMA transaction,
+marking its 'done' variable with the
+AO_DMA_ABORTED bit.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_stm32l_dma_engine">6.2. STM32L DMA Engine</h3>
+<div class="sect3">
+<h4 id="_ao_dma_alloc_2">6.2.1. ao_dma_alloc</h4>
+<div class="literalblock">
+<div class="content">
+<pre>uint8_t ao_dma_done[];
+
+void
+ao_dma_alloc(uint8_t index);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Reserve a DMA engine for exclusive use by one
+driver.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_dma_set_transfer_2">6.2.2. ao_dma_set_transfer</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_dma_set_transfer(uint8_t id,
+void *peripheral,
+void *memory,
+uint16_t count,
+uint32_t ccr);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Initializes the specified dma engine to copy
+data between 'peripheral' and 'memory' for
+'count' units. 'ccr' is a value directly out
+of the STM32L documentation and tells the DMA
+engine what the transfer unit size, direction
+and step are.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_dma_set_isr">6.2.3. ao_dma_set_isr</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_dma_set_isr(uint8_t index, void (*isr)(int))</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This sets a function to be called when the DMA
+transfer completes in lieu of setting the
+ao_dma_done bits. Use this when some work
+needs to be done when the DMA finishes that
+cannot wait until user space resumes.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_dma_start_2">6.2.4. ao_dma_start</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_dma_start(uint8_t id);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Arm the specified DMA engine and await a
+signal from either hardware or software to
+start transferring data.  'ao_dma_done[index]'
+is cleared when the DMA is started, and then
+receives the AO_DMA_DONE bit on a successful
+transfer or the AO_DMA_ABORTED bit if
+ao_dma_abort was called. Note that it is
+possible to get both bits if the transfer was
+aborted after it had finished.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_dma_done_transfer">6.2.5. ao_dma_done_transfer</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_dma_done_transfer(uint8_t id);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Signals that a specific DMA engine is done
+being used. This allows multiple drivers to
+use the same DMA engine safely.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_dma_abort_2">6.2.6. ao_dma_abort</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_dma_abort(uint8_t id)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Terminate any in-progress DMA transaction,
+marking its 'done' variable with the
+AO_DMA_ABORTED bit.</p>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_stdio_interface">7. Stdio interface</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>AltOS offers a stdio interface over USB, serial and the RF
+packet link. This provides for control of the device locally or
+remotely. This is hooked up to the stdio functions by providing
+the standard putchar/getchar/flush functions. These
+automatically multiplex the available communication channels;
+output is always delivered to the channel which provided the
+most recent input.</p>
+</div>
+<div class="sect2">
+<h3 id="_putchar">7.1. putchar</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+putchar(char c)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Delivers a single character to the current console
+device.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_getchar">7.2. getchar</h3>
+<div class="literalblock">
+<div class="content">
+<pre>char
+getchar(void)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Reads a single character from any of the available
+console devices. The current console device is set to
+that which delivered this character. This blocks until
+a character is available.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_flush">7.3. flush</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+flush(void)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Flushes the current console device output buffer. Any
+pending characters will be delivered to the target device.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_add_stdio">7.4. ao_add_stdio</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_add_stdio(char (*pollchar)(void),
+void (*putchar)(char),
+void (*flush)(void))</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This adds another console device to the available
+list.</p>
+</div>
+<div class="paragraph">
+<p>'pollchar' returns either an available character or
+AO_READ_AGAIN if none is available. Significantly, it does
+not block. The device driver must set 'ao_stdin_ready' to
+1 and call ao_wakeup(&amp;ao_stdin_ready) when it receives
+input to tell getchar that more data is available, at
+which point 'pollchar' will be called again.</p>
+</div>
+<div class="paragraph">
+<p>'putchar' queues a character for output, flushing if the output buffer is
+full. It may block in this case.</p>
+</div>
+<div class="paragraph">
+<p>'flush' forces the output buffer to be flushed. It may
+block until the buffer is delivered, but it is not
+required to do so.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_command_line_interface">8. Command line interface</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>AltOS includes a simple command line parser which is hooked up
+to the stdio interfaces permitting remote control of the
+device over USB, serial or the RF link as desired. Each
+command uses a single character to invoke it, the remaining
+characters on the line are available as parameters to the
+command.</p>
+</div>
+<div class="sect2">
+<h3 id="_ao_cmd_register">8.1. ao_cmd_register</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_cmd_register(__code struct ao_cmds *cmds)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This registers a set of commands with the command
+parser. There is a fixed limit on the number of command
+sets, the system will panic if too many are registered.
+Each command is defined by a struct ao_cmds entry:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>struct ao_cmds {
+       char            cmd;
+       void            (*func)(void);
+       const char      *help;
+};</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>'cmd' is the character naming the command. 'func' is the
+function to invoke and 'help' is a string displayed by the
+'?' command. Syntax errors found while executing 'func'
+should be indicated by modifying the global ao_cmd_status
+variable with one of the following values:</p>
+</div>
+<div class="dlist">
+<dl>
+<dt class="hdlist1">ao_cmd_success</dt>
+<dd>
+<p>The command was parsed successfully. There is no need
+to assign this value, it is the default.</p>
+</dd>
+<dt class="hdlist1">ao_cmd_lex_error</dt>
+<dd>
+<p>A token in the line was invalid, such as a number
+containing invalid characters. The low-level lexing
+functions already assign this value as needed.</p>
+</dd>
+<dt class="hdlist1">ao_syntax_error</dt>
+<dd>
+<p>The command line is invalid for some reason other than
+invalid tokens.</p>
+</dd>
+</dl>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_cmd_lex">8.2. ao_cmd_lex</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_cmd_lex(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This gets the next character out of the command line
+buffer and sticks it into ao_cmd_lex_c. At the end of
+the line, ao_cmd_lex_c will get a newline ('\n')
+character.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_cmd_put16">8.3. ao_cmd_put16</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_cmd_put16(uint16_t v);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Writes 'v' as four hexadecimal characters.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_cmd_put8">8.4. ao_cmd_put8</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_cmd_put8(uint8_t v);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Writes 'v' as two hexadecimal characters.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_cmd_white">8.5. ao_cmd_white</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_cmd_white(void)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This skips whitespace by calling ao_cmd_lex while
+ao_cmd_lex_c is either a space or tab. It does not
+skip any characters if ao_cmd_lex_c already non-white.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_cmd_hex">8.6. ao_cmd_hex</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_cmd_hex(void)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This reads a 16-bit hexadecimal value from the command
+line with optional leading whitespace. The resulting
+value is stored in ao_cmd_lex_i;</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_cmd_decimal">8.7. ao_cmd_decimal</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_cmd_decimal(void)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This reads a 32-bit decimal value from the command
+line with optional leading whitespace. The resulting
+value is stored in ao_cmd_lex_u32 and the low 16 bits
+are stored in ao_cmd_lex_i;</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_match_word">8.8. ao_match_word</h3>
+<div class="literalblock">
+<div class="content">
+<pre>uint8_t
+ao_match_word(__code char *word)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This checks to make sure that 'word' occurs on the
+command line. It does not skip leading white space. If
+'word' is found, then 1 is returned. Otherwise,
+ao_cmd_status is set to ao_cmd_syntax_error and 0 is
+returned.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_cmd_init">8.9. ao_cmd_init</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_cmd_init(void</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Initializes the command system, setting up the
+built-in commands and adding a task to run the command
+processing loop. It should be called by 'main' before
+ao_start_scheduler.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_usb_target_device">9. USB target device</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>AltOS contains a full-speed USB target device driver. It can
+be programmed to offer any kind of USB target, but to simplify
+interactions with a variety of operating systems, AltOS
+provides only a single target device profile, that of a USB
+modem which has native drivers for Linux, Windows and Mac OS
+X. It would be easy to change the code to provide an alternate
+target device if necessary.</p>
+</div>
+<div class="paragraph">
+<p>To the rest of the system, the USB device looks like a simple
+two-way byte stream. It can be hooked into the command line
+interface if desired, offering control of the device over the
+USB link. Alternatively, the functions can be accessed
+directly to provide for USB-specific I/O.</p>
+</div>
+<div class="sect2">
+<h3 id="_ao_usb_flush">9.1. ao_usb_flush</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_usb_flush(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Flushes any pending USB output. This queues an 'IN'
+packet to be delivered to the USB host if there is
+pending data, or if the last IN packet was full to
+indicate to the host that there isn&#8217;t any more pending
+data available.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_usb_putchar">9.2. ao_usb_putchar</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_usb_putchar(char c);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If there is a pending 'IN' packet awaiting delivery to
+the host, this blocks until that has been
+fetched. Then, this adds a byte to the pending IN
+packet for delivery to the USB host. If the USB packet
+is full, this queues the 'IN' packet for delivery.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_usb_pollchar">9.3. ao_usb_pollchar</h3>
+<div class="literalblock">
+<div class="content">
+<pre>char
+ao_usb_pollchar(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If there are no characters remaining in the last 'OUT'
+packet received, this returns
+AO_READ_AGAIN. Otherwise, it returns the next
+character, reporting to the host that it is ready for
+more data when the last character is gone.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_usb_getchar">9.4. ao_usb_getchar</h3>
+<div class="literalblock">
+<div class="content">
+<pre>char
+ao_usb_getchar(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This uses ao_pollchar to receive the next character,
+blocking while ao_pollchar returns AO_READ_AGAIN.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_usb_disable">9.5. ao_usb_disable</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_usb_disable(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This turns off the USB controller. It will no longer
+respond to host requests, nor return
+characters. Calling any of the i/o routines while the
+USB device is disabled is undefined, and likely to
+break things. Disabling the USB device when not needed
+saves power.</p>
+</div>
+<div class="paragraph">
+<p>Note that neither TeleDongle v0.2 nor TeleMetrum v1
+are able to signal to the USB host that they have
+disconnected, so after disabling the USB device, it&#8217;s
+likely that the cable will need to be disconnected and
+reconnected before it will work again.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_usb_enable">9.6. ao_usb_enable</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_usb_enable(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This turns the USB controller on again after it has
+been disabled. See the note above about needing to
+physically remove and re-insert the cable to get the
+host to re-initialize the USB link.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_usb_init">9.7. ao_usb_init</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_usb_init(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This turns the USB controller on, adds a task to
+handle the control end point and adds the usb I/O
+functions to the stdio system. Call this from main
+before ao_start_scheduler.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_serial_peripherals">10. Serial peripherals</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>The CC1111 provides two USART peripherals. AltOS uses one for
+asynch serial data, generally to communicate with a GPS
+device, and the other for a SPI bus. The UART is configured to
+operate in 8-bits, no parity, 1 stop bit framing. The default
+configuration has clock settings for 4800, 9600 and 57600 baud
+operation. Additional speeds can be added by computing
+appropriate clock values.</p>
+</div>
+<div class="paragraph">
+<p>To prevent loss of data, AltOS provides receive and transmit
+fifos of 32 characters each.</p>
+</div>
+<div class="sect2">
+<h3 id="_ao_serial_getchar">10.1. ao_serial_getchar</h3>
+<div class="literalblock">
+<div class="content">
+<pre>char
+ao_serial_getchar(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Returns the next character from the receive fifo, blocking
+until a character is received if the fifo is empty.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_serial_putchar">10.2. ao_serial_putchar</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_serial_putchar(char c);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Adds a character to the transmit fifo, blocking if the
+fifo is full. Starts transmitting characters.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_serial_drain">10.3. ao_serial_drain</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_serial_drain(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Blocks until the transmit fifo is empty. Used internally
+when changing serial speeds.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_serial_set_speed">10.4. ao_serial_set_speed</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_serial_set_speed(uint8_t speed);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Changes the serial baud rate to one of
+AO_SERIAL_SPEED_4800, AO_SERIAL_SPEED_9600 or
+AO_SERIAL_SPEED_57600. This first flushes the transmit
+fifo using ao_serial_drain.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_serial_init">10.5. ao_serial_init</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_serial_init(void)</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Initializes the serial peripheral. Call this from 'main'
+before jumping to ao_start_scheduler. The default speed
+setting is AO_SERIAL_SPEED_4800.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_cc1111cc1120cc1200_radio_peripheral">11. CC1111/CC1120/CC1200 Radio peripheral</h2>
+<div class="sectionbody">
+<div class="sect2">
+<h3 id="_radio_introduction">11.1. Radio Introduction</h3>
+<div class="paragraph">
+<p>The CC1111, CC1120 and CC1200 radio transceiver sends
+and receives digital packets with forward error
+correction and detection. The AltOS driver is fairly
+specific to the needs of the TeleMetrum and TeleDongle
+devices, using it for other tasks may require
+customization of the driver itself. There are three
+basic modes of operation:</p>
+</div>
+<div class="olist arabic">
+<ol class="arabic">
+<li>
+<p>Telemetry mode. In this mode, TeleMetrum transmits telemetry
+frames at a fixed rate. The frames are of fixed size. This
+is strictly a one-way communication from TeleMetrum to
+TeleDongle.</p>
+</li>
+<li>
+<p>Packet mode. In this mode, the radio is used to create a
+reliable duplex byte stream between TeleDongle and
+TeleMetrum. This is an asymmetrical protocol with
+TeleMetrum only transmitting in response to a packet sent
+from TeleDongle. Thus getting data from TeleMetrum to
+TeleDongle requires polling. The polling rate is adaptive,
+when no data has been received for a while, the rate slows
+down. The packets are checked at both ends and invalid data
+are ignored.</p>
+</li>
+</ol>
+</div>
+<div class="paragraph">
+<p>On the TeleMetrum side, the packet link is hooked into the
+stdio mechanism, providing an alternate data path for the
+command processor. It is enabled when the unit boots up in
+'idle' mode.</p>
+</div>
+<div class="paragraph">
+<p>On the TeleDongle side, the packet link is enabled with a
+command; data from the stdio package is forwarded over the
+packet link providing a connection from the USB command
+stream to the remote TeleMetrum device.</p>
+</div>
+<div class="olist arabic">
+<ol class="arabic">
+<li>
+<p>Radio Direction Finding mode. In this mode, TeleMetrum
+constructs a special packet that sounds like an audio tone
+when received by a conventional narrow-band FM
+receiver. This is designed to provide a beacon to track the
+device when other location mechanisms fail.</p>
+</li>
+</ol>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_radio_set_telemetry">11.2. ao_radio_set_telemetry</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_radio_set_telemetry(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Configures the radio to send or receive telemetry
+packets. This includes packet length, modulation scheme and
+other RF parameters. It does not include the base frequency
+or channel though. Those are set at the time of transmission
+or reception, in case the values are changed by the user.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_radio_set_packet">11.3. ao_radio_set_packet</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_radio_set_packet(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Configures the radio to send or receive packet data.  This
+includes packet length, modulation scheme and other RF
+parameters. It does not include the base frequency or
+channel though. Those are set at the time of transmission or
+reception, in case the values are changed by the user.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_radio_set_rdf">11.4. ao_radio_set_rdf</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_radio_set_rdf(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Configures the radio to send RDF 'packets'. An RDF 'packet'
+is a sequence of hex 0x55 bytes sent at a base bit rate of
+2kbps using a 5kHz deviation. All of the error correction
+and data whitening logic is turned off so that the resulting
+modulation is received as a 1kHz tone by a conventional 70cm
+FM audio receiver.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_radio_idle">11.5. ao_radio_idle</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_radio_idle(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Sets the radio device to idle mode, waiting until it reaches
+that state. This will terminate any in-progress transmit or
+receive operation.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_radio_get">11.6. ao_radio_get</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_radio_get(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Acquires the radio mutex and then configures the radio
+frequency using the global radio calibration and channel
+values.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_radio_put">11.7. ao_radio_put</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_radio_put(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Releases the radio mutex.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_ao_radio_abort">11.8. ao_radio_abort</h3>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_radio_abort(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Aborts any transmission or reception process by aborting the
+associated DMA object and calling ao_radio_idle to terminate
+the radio operation.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_radio_telemetry">11.9. Radio Telemetry</h3>
+<div class="paragraph">
+<p>In telemetry mode, you can send or receive a telemetry
+packet. The data from receiving a packet also includes the RSSI
+and status values supplied by the receiver. These are added
+after the telemetry data.</p>
+</div>
+<div class="sect3">
+<h4 id="_ao_radio_send">11.9.1. ao_radio_send</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_radio_send(__xdata struct ao_telemetry *telemetry);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This sends the specific telemetry packet, waiting for the
+transmission to complete. The radio must have been set to
+telemetry mode. This function calls ao_radio_get() before
+sending, and ao_radio_put() afterwards, to correctly
+serialize access to the radio device.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_radio_recv">11.9.2. ao_radio_recv</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_radio_recv(__xdata struct ao_radio_recv *radio);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This blocks waiting for a telemetry packet to be received.
+The radio must have been set to telemetry mode. This
+function calls ao_radio_get() before receiving, and
+ao_radio_put() afterwards, to correctly serialize access
+to the radio device. This returns non-zero if a packet was
+received, or zero if the operation was aborted (from some
+other task calling ao_radio_abort()).</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_radio_direction_finding">11.10. Radio Direction Finding</h3>
+<div class="paragraph">
+<p>In radio direction finding mode, there&#8217;s just one function to
+use</p>
+</div>
+<div class="sect3">
+<h4 id="_ao_radio_rdf">11.10.1. ao_radio_rdf</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_radio_rdf(int ms);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This sends an RDF packet lasting for the specified amount
+of time. The maximum length is 1020 ms.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_radio_packet_mode">11.11. Radio Packet Mode</h3>
+<div class="paragraph">
+<p>Packet mode is asymmetrical and is configured at compile time
+for either master or slave mode (but not both). The basic I/O
+functions look the same at both ends, but the internals are
+different, along with the initialization steps.</p>
+</div>
+<div class="sect3">
+<h4 id="_ao_packet_putchar">11.11.1. ao_packet_putchar</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_packet_putchar(char c);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If the output queue is full, this first blocks waiting for
+that data to be delivered. Then, queues a character for
+packet transmission. On the master side, this will
+transmit a packet if the output buffer is full. On the
+slave side, any pending data will be sent the next time
+the master polls for data.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_packet_pollchar">11.11.2. ao_packet_pollchar</h4>
+<div class="literalblock">
+<div class="content">
+<pre>char
+ao_packet_pollchar(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This returns a pending input character if available,
+otherwise returns AO_READ_AGAIN. On the master side, if
+this empties the buffer, it triggers a poll for more data.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_packet_slave_start">11.11.3. ao_packet_slave_start</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_packet_slave_start(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This is available only on the slave side and starts a task
+to listen for packet data.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_packet_slave_stop">11.11.4. ao_packet_slave_stop</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_packet_slave_stop(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Disables the packet slave task, stopping the radio receiver.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_packet_slave_init">11.11.5. ao_packet_slave_init</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_packet_slave_init(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Adds the packet stdio functions to the stdio package so
+that when packet slave mode is enabled, characters will
+get send and received through the stdio functions.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_ao_packet_master_init">11.11.6. ao_packet_master_init</h4>
+<div class="literalblock">
+<div class="content">
+<pre>void
+ao_packet_master_init(void);</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Adds the 'p' packet forward command to start packet mode.</p>
+</div>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div id="footer">
+<div id="footer-text">
+Last updated 2020-10-22 16:37:05 -0600
+</div>
+</div>
+</body>
+</html>
\ No newline at end of file