1 <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="idm46302637457168"></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="idm46302611036384"></a><p>
2 This document is released under the terms of the
3 <a class="ulink" href="http://creativecommons.org/licenses/by-sa/3.0/" target="_top">
4 Creative Commons ShareAlike 3.0
7 </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="#idm46302610177888">1. Overview</a></span></dt><dt><span class="chapter"><a href="#idm46302610940496">2. AltOS Porting Layer</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302610938992">1. Low-level CPU operations</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302610937760">1.1. ao_arch_block_interrupts/ao_arch_release_interrupts</a></span></dt><dt><span class="section"><a href="#idm46302610935712">1.2. ao_arch_save_regs, ao_arch_save_stack,
8 ao_arch_restore_stack</a></span></dt><dt><span class="section"><a href="#idm46302610933424">1.3. ao_arch_wait_interupt</a></span></dt></dl></dd><dt><span class="section"><a href="#idm46302610930880">2. GPIO operations</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302610929712">2.1. GPIO setup</a></span></dt><dt><span class="section"><a href="#idm46302611109712">2.2. Reading and writing GPIO pins</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#idm46302611104464">3. Programming the 8051 with SDCC</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302611102368">1. 8051 memory spaces</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302611100432">1.1. __data</a></span></dt><dt><span class="section"><a href="#idm46302611098256">1.2. __idata</a></span></dt><dt><span class="section"><a href="#idm46302611096816">1.3. __xdata</a></span></dt><dt><span class="section"><a href="#idm46302611095408">1.4. __pdata</a></span></dt><dt><span class="section"><a href="#idm46302611093904">1.5. __code</a></span></dt><dt><span class="section"><a href="#idm46302611092464">1.6. __bit</a></span></dt><dt><span class="section"><a href="#idm46302611090960">1.7. __sfr, __sfr16, __sfr32, __sbit</a></span></dt></dl></dd><dt><span class="section"><a href="#idm46302611089424">2. Function calls on the 8051</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302611087952">2.1. __reentrant functions</a></span></dt><dt><span class="section"><a href="#idm46302611085760">2.2. Non __reentrant functions</a></span></dt><dt><span class="section"><a href="#idm46302611083616">2.3. __interrupt functions</a></span></dt><dt><span class="section"><a href="#idm46302611082048">2.4. __critical functions and statements</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#idm46302611079984">4. Task functions</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302611078928">1. ao_add_task</a></span></dt><dt><span class="section"><a href="#idm46302611076672">2. ao_exit</a></span></dt><dt><span class="section"><a href="#idm46302605188160">3. ao_sleep</a></span></dt><dt><span class="section"><a href="#idm46302605184560">4. ao_wakeup</a></span></dt><dt><span class="section"><a href="#idm46302605181568">5. ao_alarm</a></span></dt><dt><span class="section"><a href="#idm46302605178288">6. ao_start_scheduler</a></span></dt><dt><span class="section"><a href="#idm46302605176320">7. ao_clock_init</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idm46302605174224">5. Timer Functions</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605172720">1. ao_time</a></span></dt><dt><span class="section"><a href="#idm46302605170688">2. ao_delay</a></span></dt><dt><span class="section"><a href="#idm46302605168768">3. ao_timer_set_adc_interval</a></span></dt><dt><span class="section"><a href="#idm46302605166640">4. ao_timer_init</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idm46302605164448">6. AltOS Mutexes</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605162576">1. ao_mutex_get</a></span></dt><dt><span class="section"><a href="#idm46302605160736">2. ao_mutex_put</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idm46302605158672">7. DMA engine</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605155232">1. CC1111 DMA Engine</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605154560">1.1. ao_dma_alloc</a></span></dt><dt><span class="section"><a href="#idm46302605152384">1.2. ao_dma_set_transfer</a></span></dt><dt><span class="section"><a href="#idm46302605150112">1.3. ao_dma_start</a></span></dt><dt><span class="section"><a href="#idm46302605148096">1.4. ao_dma_trigger</a></span></dt><dt><span class="section"><a href="#idm46302605146176">1.5. ao_dma_abort</a></span></dt></dl></dd><dt><span class="section"><a href="#idm46302605144032">2. STM32L DMA Engine</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605143360">2.1. ao_dma_alloc</a></span></dt><dt><span class="section"><a href="#idm46302605141360">2.2. ao_dma_set_transfer</a></span></dt><dt><span class="section"><a href="#idm46302605139104">2.3. ao_dma_set_isr</a></span></dt><dt><span class="section"><a href="#idm46302605136944">2.4. ao_dma_start</a></span></dt><dt><span class="section"><a href="#idm46302605134640">2.5. ao_dma_done_transfer</a></span></dt><dt><span class="section"><a href="#idm46302605132656">2.6. ao_dma_abort</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#idm46302605130384">8. Stdio interface</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605128880">1. putchar</a></span></dt><dt><span class="section"><a href="#idm46302605126912">2. getchar</a></span></dt><dt><span class="section"><a href="#idm46302605124816">3. flush</a></span></dt><dt><span class="section"><a href="#idm46302605122784">4. ao_add_stdio</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idm46302605118752">9. Command line interface</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605117344">1. ao_cmd_register</a></span></dt><dt><span class="section"><a href="#idm46302605108608">2. ao_cmd_lex</a></span></dt><dt><span class="section"><a href="#idm46302605106528">3. ao_cmd_put16</a></span></dt><dt><span class="section"><a href="#idm46302605104688">4. ao_cmd_put8</a></span></dt><dt><span class="section"><a href="#idm46302605102800">5. ao_cmd_white</a></span></dt><dt><span class="section"><a href="#idm46302605100736">6. ao_cmd_hex</a></span></dt><dt><span class="section"><a href="#idm46302605098688">7. ao_cmd_decimal</a></span></dt><dt><span class="section"><a href="#idm46302605096592">8. ao_match_word</a></span></dt><dt><span class="section"><a href="#idm46302605094512">9. ao_cmd_init</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idm46302605092288">10. USB target device</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605090064">1. ao_usb_flush</a></span></dt><dt><span class="section"><a href="#idm46302605087920">2. ao_usb_putchar</a></span></dt><dt><span class="section"><a href="#idm46302605085744">3. ao_usb_pollchar</a></span></dt><dt><span class="section"><a href="#idm46302605083600">4. ao_usb_getchar</a></span></dt><dt><span class="section"><a href="#idm46302605081584">5. ao_usb_disable</a></span></dt><dt><span class="section"><a href="#idm46302605078752">6. ao_usb_enable</a></span></dt><dt><span class="section"><a href="#idm46302605076640">7. ao_usb_init</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idm46302605074416">11. Serial peripherals</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605072400">1. ao_serial_getchar</a></span></dt><dt><span class="section"><a href="#idm46302605070368">2. ao_serial_putchar</a></span></dt><dt><span class="section"><a href="#idm46302605068400">3. ao_serial_drain</a></span></dt><dt><span class="section"><a href="#idm46302605066400">4. ao_serial_set_speed</a></span></dt><dt><span class="section"><a href="#idm46302605064368">5. ao_serial_init</a></span></dt></dl></dd><dt><span class="chapter"><a href="#idm46302605062176">12. CC1111 Radio peripheral</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605061504">1. Radio Introduction</a></span></dt><dt><span class="section"><a href="#idm46302605054896">2. ao_radio_set_telemetry</a></span></dt><dt><span class="section"><a href="#idm46302605052800">3. ao_radio_set_packet</a></span></dt><dt><span class="section"><a href="#idm46302605050704">4. ao_radio_set_rdf</a></span></dt><dt><span class="section"><a href="#idm46302605048576">5. ao_radio_idle</a></span></dt><dt><span class="section"><a href="#idm46302605046640">6. ao_radio_get</a></span></dt><dt><span class="section"><a href="#idm46302605044720">7. ao_radio_put</a></span></dt><dt><span class="section"><a href="#idm46302605042944">8. ao_radio_abort</a></span></dt><dt><span class="section"><a href="#idm46302605040992">9. Radio Telemetry</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605039728">9.1. ao_radio_send</a></span></dt><dt><span class="section"><a href="#idm46302605037568">9.2. ao_radio_recv</a></span></dt></dl></dd><dt><span class="section"><a href="#idm46302605035168">10. Radio Direction Finding</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605034048">10.1. ao_radio_rdf</a></span></dt></dl></dd><dt><span class="section"><a href="#idm46302605032016">11. Radio Packet Mode</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605030736">11.1. ao_packet_putchar</a></span></dt><dt><span class="section"><a href="#idm46302605028624">11.2. ao_packet_pollchar</a></span></dt><dt><span class="section"><a href="#idm46302605026656">11.3. ao_packet_slave_start</a></span></dt><dt><span class="section"><a href="#idm46302605024768">11.4. ao_packet_slave_stop</a></span></dt><dt><span class="section"><a href="#idm46302605022912">11.5. ao_packet_slave_init</a></span></dt><dt><span class="section"><a href="#idm46302605020944">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="idm46302610177888"></a>Chapter 1. Overview</h1></div></div></div><p>
9 AltOS is a operating system built for a variety of
10 microcontrollers used in Altus Metrum devices. It has a simple
11 porting layer for each CPU while providing a convenient
12 operating enviroment for the developer. AltOS currently
13 supports three different CPUs:
14 </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
15 STM32L series from ST Microelectronics. This ARM Cortex-M3
16 based microcontroller offers low power consumption and a
17 wide variety of built-in peripherals. Altus Metrum uses
18 this in the TeleMega, MegaDongle and TeleLCO projects.
19 </p></li><li class="listitem"><p>
20 CC1111 from Texas Instruments. This device includes a
21 fabulous 10mW digital RF transceiver along with an
22 8051-compatible processor core and a range of
23 peripherals. This is used in the TeleMetrum, TeleMini,
24 TeleDongle and TeleFire projects which share the need for
25 a small microcontroller and an RF interface.
26 </p></li><li class="listitem"><p>
27 ATmega32U4 from Atmel. This 8-bit AVR microcontroller is
28 one of the many used to create Arduino boards. The 32U4
29 includes a USB interface, making it easy to connect to
30 other computers. Altus Metrum used this in prototypes of
31 the TeleScience and TelePyro boards; those have been
32 switched to the STM32L which is more capable and cheaper.
33 </p></li></ul></div><p>
34 Among the features of AltOS are:
35 </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>Multi-tasking. While microcontrollers often don't
36 provide separate address spaces, it's often easier to write
37 code that operates in separate threads instead of tying
38 everything into one giant event loop.
39 </p></li><li class="listitem"><p>Non-preemptive. This increases latency for thread
40 switching but reduces the number of places where context
41 switching can occur. It also simplifies the operating system
42 design somewhat. Nothing in the target system (rocket flight
43 control) has tight timing requirements, and so this seems like
44 a reasonable compromise.
45 </p></li><li class="listitem"><p>Sleep/wakeup scheduling. Taken directly from ancient
46 Unix designs, these two provide the fundemental scheduling
47 primitive within AltOS.
48 </p></li><li class="listitem"><p>Mutexes. As a locking primitive, mutexes are easier to
49 use than semaphores, at least in my experience.
50 </p></li><li class="listitem"><p>Timers. Tasks can set an alarm which will abort any
51 pending sleep, allowing operations to time-out instead of
53 </p></li></ul></div><p>
55 The device drivers and other subsystems in AltOS are
56 conventionally enabled by invoking their _init() function from
57 the 'main' function before that calls
58 ao_start_scheduler(). These functions initialize the pin
59 assignments, add various commands to the command processor and
60 may add tasks to the scheduler to handle the device. A typical
61 main program, thus, looks like:
62 </p><pre class="programlisting">
68 /* Turn on the LED until the system is stable */
69 ao_led_init(LEDS_AVAILABLE);
70 ao_led_on(AO_LED_RED);
74 ao_monitor_init(AO_LED_GREEN, TRUE);
75 ao_rssi_init(AO_LED_RED);
77 ao_packet_slave_init();
78 ao_packet_master_init();
86 As you can see, a long sequence of subsystems are initialized
87 and then the scheduler is started.
88 </p></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idm46302610940496"></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="#idm46302610938992">1. Low-level CPU operations</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302610937760">1.1. ao_arch_block_interrupts/ao_arch_release_interrupts</a></span></dt><dt><span class="section"><a href="#idm46302610935712">1.2. ao_arch_save_regs, ao_arch_save_stack,
89 ao_arch_restore_stack</a></span></dt><dt><span class="section"><a href="#idm46302610933424">1.3. ao_arch_wait_interupt</a></span></dt></dl></dd><dt><span class="section"><a href="#idm46302610930880">2. GPIO operations</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302610929712">2.1. GPIO setup</a></span></dt><dt><span class="section"><a href="#idm46302611109712">2.2. Reading and writing GPIO pins</a></span></dt></dl></dd></dl></div><p>
90 AltOS provides a CPU-independent interface to various common
91 microcontroller subsystems, including GPIO pins, interrupts,
92 SPI, I2C, USB and asynchronous serial interfaces. By making
93 these CPU-independent, device drivers, generic OS and
94 application code can all be written that work on any supported
95 CPU. Many of the architecture abstraction interfaces are
96 prefixed with ao_arch.
97 </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302610938992"></a>1. Low-level CPU operations</h2></div></div></div><p>
98 These primitive operations provide the abstraction needed to
99 run the multi-tasking framework while providing reliable
101 </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302610937760"></a>1.1. ao_arch_block_interrupts/ao_arch_release_interrupts</h3></div></div></div><pre class="programlisting">
103 ao_arch_block_interrupts(void);
106 ao_arch_release_interrupts(void);
108 These disable/enable interrupt delivery, they may not
109 discard any interrupts. Use these for sections of code that
110 must be atomic with respect to any code run from an
112 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302610935712"></a>1.2. ao_arch_save_regs, ao_arch_save_stack,
113 ao_arch_restore_stack</h3></div></div></div><pre class="programlisting">
115 ao_arch_save_regs(void);
118 ao_arch_save_stack(void);
121 ao_arch_restore_stack(void);
123 These provide all of the support needed to switch between
124 tasks.. ao_arch_save_regs must save all CPU registers to the
125 current stack, including the interrupt enable
126 state. ao_arch_save_stack records the current stack location
127 in the current ao_task structure. ao_arch_restore_stack
128 switches back to the saved stack, restores all registers and
129 branches to the saved return address.
130 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302610933424"></a>1.3. ao_arch_wait_interupt</h3></div></div></div><pre class="programlisting">
131 #define ao_arch_wait_interrupt()
133 This stops the CPU, leaving clocks and interrupts
134 enabled. When an interrupt is received, this must wake up
135 and handle the interrupt. ao_arch_wait_interrupt is entered
136 with interrupts disabled to ensure that there is no gap
137 between determining that no task wants to run and idling the
138 CPU. It must sleep the CPU, process interrupts and then
139 disable interrupts again. If the CPU doesn't have any
140 reduced power mode, this must at the least allow pending
141 interrupts to be processed.
142 </p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302610930880"></a>2. GPIO operations</h2></div></div></div><p>
143 These functions provide an abstract interface to configure and
144 manipulate GPIO pins.
145 </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302610929712"></a>2.1. GPIO setup</h3></div></div></div><p>
146 These macros may be invoked at system initialization time to
147 configure pins as needed for system operation. One tricky
148 aspect is that some chips provide direct access to specific
149 GPIO pins while others only provide access to a whole
150 register full of pins. To support this, the GPIO macros
151 provide both port+bit and pin arguments. Simply define the
152 arguments needed for the target platform and leave the
154 </p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idm46302610927744"></a>2.1.1. ao_enable_output</h4></div></div></div><pre class="programlisting">
155 #define ao_enable_output(port, bit, pin, value)
157 Set the specified port+bit (also called 'pin') for output,
158 initializing to the specified value. The macro must avoid
159 driving the pin with the opposite value if at all
161 </p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idm46302611115152"></a>2.1.2. ao_enable_input</h4></div></div></div><pre class="programlisting">
162 #define ao_enable_input(port, bit, mode)
164 Sets the specified port/bit to be an input pin. 'mode' is
165 a combination of one or more of the following. Note that
166 some platforms may not support the desired mode. In that
167 case, the value will not be defined so that the program
168 will fail to compile.
169 </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
170 AO_EXTI_MODE_PULL_UP. Apply a pull-up to the pin; a
171 disconnected pin will read as 1.
172 </p></li><li class="listitem"><p>
173 AO_EXTI_MODE_PULL_DOWN. Apply a pull-down to the pin;
174 a disconnected pin will read as 0.
175 </p></li><li class="listitem"><p>
176 0. Don't apply either a pull-up or pull-down. A
177 disconnected pin will read an undetermined value.
178 </p></li></ul></div><p>
179 </p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611109712"></a>2.2. Reading and writing GPIO pins</h3></div></div></div><p>
180 These macros read and write individual GPIO pins.
181 </p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idm46302611108640"></a>2.2.1. ao_gpio_set</h4></div></div></div><pre class="programlisting">
182 #define ao_gpio_set(port, bit, pin, value)
184 Sets the specified port/bit or pin to the indicated value
185 </p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idm46302611106800"></a>2.2.2. ao_gpio_get</h4></div></div></div><pre class="programlisting">
186 #define ao_gpio_get(port, bit, pin)
188 Returns either 1 or 0 depending on whether the input to
189 the pin is high or low.
190 </p></div></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idm46302611104464"></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="#idm46302611102368">1. 8051 memory spaces</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302611100432">1.1. __data</a></span></dt><dt><span class="section"><a href="#idm46302611098256">1.2. __idata</a></span></dt><dt><span class="section"><a href="#idm46302611096816">1.3. __xdata</a></span></dt><dt><span class="section"><a href="#idm46302611095408">1.4. __pdata</a></span></dt><dt><span class="section"><a href="#idm46302611093904">1.5. __code</a></span></dt><dt><span class="section"><a href="#idm46302611092464">1.6. __bit</a></span></dt><dt><span class="section"><a href="#idm46302611090960">1.7. __sfr, __sfr16, __sfr32, __sbit</a></span></dt></dl></dd><dt><span class="section"><a href="#idm46302611089424">2. Function calls on the 8051</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302611087952">2.1. __reentrant functions</a></span></dt><dt><span class="section"><a href="#idm46302611085760">2.2. Non __reentrant functions</a></span></dt><dt><span class="section"><a href="#idm46302611083616">2.3. __interrupt functions</a></span></dt><dt><span class="section"><a href="#idm46302611082048">2.4. __critical functions and statements</a></span></dt></dl></dd></dl></div><p>
191 The 8051 is a primitive 8-bit processor, designed in the mists
192 of time in as few transistors as possible. The architecture is
193 highly irregular and includes several separate memory
194 spaces. Furthermore, accessing stack variables is slow, and the
195 stack itself is of limited size. While SDCC papers over the
196 instruction set, it is not completely able to hide the memory
197 architecture from the application designer.
199 When built on other architectures, the various SDCC-specific
200 symbols are #defined as empty strings so they don't affect the compiler.
201 </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302611102368"></a>1. 8051 memory spaces</h2></div></div></div><p>
202 The __data/__xdata/__code memory spaces below were completely
203 separate in the original 8051 design. In the cc1111, this
204 isn't true—they all live in a single unified 64kB address
205 space, and so it's possible to convert any address into a
206 unique 16-bit address. SDCC doesn't know this, and so a
207 'global' address to SDCC consumes 3 bytes of memory, 1 byte as
208 a tag indicating the memory space and 2 bytes of offset within
209 that space. AltOS avoids these 3-byte addresses as much as
210 possible; using them involves a function call per byte
211 access. The result is that nearly every variable declaration
212 is decorated with a memory space identifier which clutters the
213 code but makes the resulting code far smaller and more
215 </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611100432"></a>1.1. __data</h3></div></div></div><p>
216 The 8051 can directly address these 128 bytes of
217 memory. This makes them precious so they should be
218 reserved for frequently addressed values. Oh, just to
219 confuse things further, the 8 general registers in the
220 CPU are actually stored in this memory space. There are
221 magic instructions to 'bank switch' among 4 banks of
222 these registers located at 0x00 - 0x1F. AltOS uses only
223 the first bank at 0x00 - 0x07, leaving the other 24
224 bytes available for other data.
225 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611098256"></a>1.2. __idata</h3></div></div></div><p>
226 There are an additional 128 bytes of internal memory
227 that share the same address space as __data but which
228 cannot be directly addressed. The stack normally
229 occupies this space and so AltOS doesn't place any
231 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611096816"></a>1.3. __xdata</h3></div></div></div><p>
232 This is additional general memory accessed through a
233 single 16-bit address register. The CC1111F32 has 32kB
234 of memory available here. Most program data should live
235 in this memory space.
236 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611095408"></a>1.4. __pdata</h3></div></div></div><p>
237 This is an alias for the first 256 bytes of __xdata
238 memory, but uses a shorter addressing mode with
239 single global 8-bit value for the high 8 bits of the
240 address and any of several 8-bit registers for the low 8
241 bits. AltOS uses a few bits of this memory, it should
243 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611093904"></a>1.5. __code</h3></div></div></div><p>
244 All executable code must live in this address space, but
245 you can stick read-only data here too. It is addressed
246 using the 16-bit address register and special 'code'
247 access opcodes. Anything read-only should live in this space.
248 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611092464"></a>1.6. __bit</h3></div></div></div><p>
249 The 8051 has 128 bits of bit-addressible memory that
250 lives in the __data segment from 0x20 through
251 0x2f. Special instructions access these bits
252 in a single atomic operation. This isn't so much a
253 separate address space as a special addressing mode for
254 a few bytes in the __data segment.
255 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611090960"></a>1.7. __sfr, __sfr16, __sfr32, __sbit</h3></div></div></div><p>
256 Access to physical registers in the device use this mode
257 which declares the variable name, its type and the
258 address it lives at. No memory is allocated for these
260 </p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302611089424"></a>2. Function calls on the 8051</h2></div></div></div><p>
261 Because stack addressing is expensive, and stack space
262 limited, the default function call declaration in SDCC
263 allocates all parameters and local variables in static global
264 memory. Just like fortran. This makes these functions
265 non-reentrant, and also consume space for parameters and
266 locals even when they are not running. The benefit is smaller
267 code and faster execution.
268 </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611087952"></a>2.1. __reentrant functions</h3></div></div></div><p>
269 All functions which are re-entrant, either due to recursion
270 or due to a potential context switch while executing, should
271 be marked as __reentrant so that their parameters and local
272 variables get allocated on the stack. This ensures that
273 these values are not overwritten by another invocation of
276 Functions which use significant amounts of space for
277 arguments and/or local variables and which are not often
278 invoked can also be marked as __reentrant. The resulting
279 code will be larger, but the savings in memory are
280 frequently worthwhile.
281 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611085760"></a>2.2. Non __reentrant functions</h3></div></div></div><p>
282 All parameters and locals in non-reentrant functions can
283 have data space decoration so that they are allocated in
284 __xdata, __pdata or __data space as desired. This can avoid
285 consuming __data space for infrequently used variables in
286 frequently used functions.
288 All library functions called by SDCC, including functions
289 for multiplying and dividing large data types, are
290 non-reentrant. Because of this, interrupt handlers must not
291 invoke any library functions, including the multiply and
293 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611083616"></a>2.3. __interrupt functions</h3></div></div></div><p>
294 Interrupt functions are declared with with an __interrupt
295 decoration that includes the interrupt number. SDCC saves
296 and restores all of the registers in these functions and
297 uses the 'reti' instruction at the end so that they operate
298 as stand-alone interrupt handlers. Interrupt functions may
299 call the ao_wakeup function to wake AltOS tasks.
300 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302611082048"></a>2.4. __critical functions and statements</h3></div></div></div><p>
301 SDCC has built-in support for suspending interrupts during
302 critical code. Functions marked as __critical will have
303 interrupts suspended for the whole period of
304 execution. Individual statements may also be marked as
305 __critical which blocks interrupts during the execution of
306 that statement. Keeping critical sections as short as
307 possible is key to ensuring that interrupts are handled as
308 quickly as possible. AltOS doesn't use this form in shared
309 code as other compilers wouldn't know what to do. Use
310 ao_arch_block_interrupts and ao_arch_release_interrupts instead.
311 </p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idm46302611079984"></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="#idm46302611078928">1. ao_add_task</a></span></dt><dt><span class="section"><a href="#idm46302611076672">2. ao_exit</a></span></dt><dt><span class="section"><a href="#idm46302605188160">3. ao_sleep</a></span></dt><dt><span class="section"><a href="#idm46302605184560">4. ao_wakeup</a></span></dt><dt><span class="section"><a href="#idm46302605181568">5. ao_alarm</a></span></dt><dt><span class="section"><a href="#idm46302605178288">6. ao_start_scheduler</a></span></dt><dt><span class="section"><a href="#idm46302605176320">7. ao_clock_init</a></span></dt></dl></div><p>
312 This chapter documents how to create, destroy and schedule AltOS tasks.
313 </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302611078928"></a>1. ao_add_task</h2></div></div></div><pre class="programlisting">
315 ao_add_task(__xdata struct ao_task * task,
319 This initializes the statically allocated task structure,
320 assigns a name to it (not used for anything but the task
321 display), and the start address. It does not switch to the
322 new task. 'start' must not ever return; there is no place
324 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302611076672"></a>2. ao_exit</h2></div></div></div><pre class="programlisting">
328 This terminates the current task.
329 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605188160"></a>3. ao_sleep</h2></div></div></div><pre class="programlisting">
331 ao_sleep(__xdata void *wchan)
333 This suspends the current task until 'wchan' is signaled
334 by ao_wakeup, or until the timeout, set by ao_alarm,
335 fires. If 'wchan' is signaled, ao_sleep returns 0, otherwise
336 it returns 1. This is the only way to switch to another task.
338 Because ao_wakeup wakes every task waiting on a particular
339 location, ao_sleep should be used in a loop that first checks
340 the desired condition, blocks in ao_sleep and then rechecks
341 until the condition is satisfied. If the location may be
342 signaled from an interrupt handler, the code will need to
343 block interrupts around the block of code. Here's a complete
345 </p><pre class="programlisting">
346 ao_arch_block_interrupts();
347 while (!ao_radio_done)
348 ao_sleep(&ao_radio_done);
349 ao_arch_release_interrupts();
351 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605184560"></a>4. ao_wakeup</h2></div></div></div><pre class="programlisting">
353 ao_wakeup(__xdata void *wchan)
355 Wake all tasks blocked on 'wchan'. This makes them
356 available to be run again, but does not actually switch
357 to another task. Here's an example of using this:
358 </p><pre class="programlisting">
359 if (RFIF & RFIF_IM_DONE) {
361 ao_wakeup(&ao_radio_done);
362 RFIF &= ~RFIF_IM_DONE;
365 Note that this need not block interrupts as the ao_sleep block
366 can only be run from normal mode, and so this sequence can
367 never be interrupted with execution of the other sequence.
368 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605181568"></a>5. ao_alarm</h2></div></div></div><pre class="programlisting">
370 ao_alarm(uint16_t delay);
373 ao_clear_alarm(void);
375 Schedules an alarm to fire in at least 'delay' ticks. If the
376 task is asleep when the alarm fires, it will wakeup and
377 ao_sleep will return 1. ao_clear_alarm resets any pending
378 alarm so that it doesn't fire at some arbitrary point in the
380 </p><pre class="programlisting">
381 ao_alarm(ao_packet_master_delay);
382 ao_arch_block_interrupts();
383 while (!ao_radio_dma_done)
384 if (ao_sleep(&ao_radio_dma_done) != 0)
386 ao_arch_release_interrupts();
389 In this example, a timeout is set before waiting for
390 incoming radio data. If no data is received before the
391 timeout fires, ao_sleep will return 1 and then this code
392 will abort the radio receive operation.
393 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605178288"></a>6. ao_start_scheduler</h2></div></div></div><pre class="programlisting">
395 ao_start_scheduler(void);
397 This is called from 'main' when the system is all
398 initialized and ready to run. It will not return.
399 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605176320"></a>7. ao_clock_init</h2></div></div></div><pre class="programlisting">
403 This initializes the main CPU clock and switches to it.
404 </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idm46302605174224"></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="#idm46302605172720">1. ao_time</a></span></dt><dt><span class="section"><a href="#idm46302605170688">2. ao_delay</a></span></dt><dt><span class="section"><a href="#idm46302605168768">3. ao_timer_set_adc_interval</a></span></dt><dt><span class="section"><a href="#idm46302605166640">4. ao_timer_init</a></span></dt></dl></div><p>
405 AltOS sets up one of the CPU timers to run at 100Hz and
406 exposes this tick as the fundemental unit of time. At each
407 interrupt, AltOS increments the counter, and schedules any tasks
408 waiting for that time to pass, then fires off the sensors to
409 collect current data readings. Doing this from the ISR ensures
410 that the values are sampled at a regular rate, independent
411 of any scheduling jitter.
412 </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605172720"></a>1. ao_time</h2></div></div></div><pre class="programlisting">
416 Returns the current system tick count. Note that this is
417 only a 16 bit value, and so it wraps every 655.36 seconds.
418 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605170688"></a>2. ao_delay</h2></div></div></div><pre class="programlisting">
420 ao_delay(uint16_t ticks);
422 Suspend the current task for at least 'ticks' clock units.
423 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605168768"></a>3. ao_timer_set_adc_interval</h2></div></div></div><pre class="programlisting">
425 ao_timer_set_adc_interval(uint8_t interval);
427 This sets the number of ticks between ADC samples. If set
428 to 0, no ADC samples are generated. AltOS uses this to
429 slow down the ADC sampling rate to save power.
430 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605166640"></a>4. ao_timer_init</h2></div></div></div><pre class="programlisting">
434 This turns on the 100Hz tick. It is required for any of the
435 time-based functions to work. It should be called by 'main'
436 before ao_start_scheduler.
437 </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idm46302605164448"></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="#idm46302605162576">1. ao_mutex_get</a></span></dt><dt><span class="section"><a href="#idm46302605160736">2. ao_mutex_put</a></span></dt></dl></div><p>
438 AltOS provides mutexes as a basic synchronization primitive. Each
439 mutexes is simply a byte of memory which holds 0 when the mutex
440 is free or the task id of the owning task when the mutex is
441 owned. Mutex calls are checked—attempting to acquire a mutex
442 already held by the current task or releasing a mutex not held
443 by the current task will both cause a panic.
444 </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605162576"></a>1. ao_mutex_get</h2></div></div></div><pre class="programlisting">
446 ao_mutex_get(__xdata uint8_t *mutex);
448 Acquires the specified mutex, blocking if the mutex is
449 owned by another task.
450 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605160736"></a>2. ao_mutex_put</h2></div></div></div><pre class="programlisting">
452 ao_mutex_put(__xdata uint8_t *mutex);
454 Releases the specified mutex, waking up all tasks waiting
456 </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idm46302605158672"></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="#idm46302605155232">1. CC1111 DMA Engine</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605154560">1.1. ao_dma_alloc</a></span></dt><dt><span class="section"><a href="#idm46302605152384">1.2. ao_dma_set_transfer</a></span></dt><dt><span class="section"><a href="#idm46302605150112">1.3. ao_dma_start</a></span></dt><dt><span class="section"><a href="#idm46302605148096">1.4. ao_dma_trigger</a></span></dt><dt><span class="section"><a href="#idm46302605146176">1.5. ao_dma_abort</a></span></dt></dl></dd><dt><span class="section"><a href="#idm46302605144032">2. STM32L DMA Engine</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605143360">2.1. ao_dma_alloc</a></span></dt><dt><span class="section"><a href="#idm46302605141360">2.2. ao_dma_set_transfer</a></span></dt><dt><span class="section"><a href="#idm46302605139104">2.3. ao_dma_set_isr</a></span></dt><dt><span class="section"><a href="#idm46302605136944">2.4. ao_dma_start</a></span></dt><dt><span class="section"><a href="#idm46302605134640">2.5. ao_dma_done_transfer</a></span></dt><dt><span class="section"><a href="#idm46302605132656">2.6. ao_dma_abort</a></span></dt></dl></dd></dl></div><p>
457 The CC1111 and STM32L both contain a useful bit of extra
458 hardware in the form of a number of programmable DMA
459 engines. They can be configured to copy data in memory, or
460 between memory and devices (or even between two devices). AltOS
461 exposes a general interface to this hardware and uses it to
462 handle both internal and external devices.
464 Because the CC1111 and STM32L DMA engines are different, the
465 interface to them is also different. As the DMA engines are
466 currently used to implement platform-specific drivers, this
469 Code using a DMA engine should allocate one at startup
470 time. There is no provision to free them, and if you run out,
471 AltOS will simply panic.
473 During operation, the DMA engine is initialized with the
474 transfer parameters. Then it is started, at which point it
475 awaits a suitable event to start copying data. When copying data
476 from hardware to memory, that trigger event is supplied by the
477 hardware device. When copying data from memory to hardware, the
478 transfer is usually initiated by software.
479 </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605155232"></a>1. CC1111 DMA Engine</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605154560"></a>1.1. ao_dma_alloc</h3></div></div></div><pre class="programlisting">
481 ao_dma_alloc(__xdata uint8_t *done)
483 Allocate a DMA engine, returning the identifier. 'done' is
484 cleared when the DMA is started, and then receives the
485 AO_DMA_DONE bit on a successful transfer or the
486 AO_DMA_ABORTED bit if ao_dma_abort was called. Note that it
487 is possible to get both bits if the transfer was aborted
488 after it had finished.
489 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605152384"></a>1.2. ao_dma_set_transfer</h3></div></div></div><pre class="programlisting">
491 ao_dma_set_transfer(uint8_t id,
492 void __xdata *srcaddr,
493 void __xdata *dstaddr,
498 Initializes the specified dma engine to copy data
499 from 'srcaddr' to 'dstaddr' for 'count' units. cfg0 and
500 cfg1 are values directly out of the CC1111 documentation
501 and tell the DMA engine what the transfer unit size,
502 direction and step are.
503 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605150112"></a>1.3. ao_dma_start</h3></div></div></div><pre class="programlisting">
505 ao_dma_start(uint8_t id);
507 Arm the specified DMA engine and await a signal from
508 either hardware or software to start transferring data.
509 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605148096"></a>1.4. ao_dma_trigger</h3></div></div></div><pre class="programlisting">
511 ao_dma_trigger(uint8_t id)
513 Trigger the specified DMA engine to start copying data.
514 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605146176"></a>1.5. ao_dma_abort</h3></div></div></div><pre class="programlisting">
516 ao_dma_abort(uint8_t id)
518 Terminate any in-progress DMA transaction, marking its
519 'done' variable with the AO_DMA_ABORTED bit.
520 </p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605144032"></a>2. STM32L DMA Engine</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605143360"></a>2.1. ao_dma_alloc</h3></div></div></div><pre class="programlisting">
521 uint8_t ao_dma_done[];
524 ao_dma_alloc(uint8_t index);
526 Reserve a DMA engine for exclusive use by one
528 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605141360"></a>2.2. ao_dma_set_transfer</h3></div></div></div><pre class="programlisting">
530 ao_dma_set_transfer(uint8_t id,
536 Initializes the specified dma engine to copy data between
537 'peripheral' and 'memory' for 'count' units. 'ccr' is a
538 value directly out of the STM32L documentation and tells the
539 DMA engine what the transfer unit size, direction and step
541 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605139104"></a>2.3. ao_dma_set_isr</h3></div></div></div><pre class="programlisting">
543 ao_dma_set_isr(uint8_t index, void (*isr)(int))
545 This sets a function to be called when the DMA transfer
546 completes in lieu of setting the ao_dma_done bits. Use this
547 when some work needs to be done when the DMA finishes that
548 cannot wait until user space resumes.
549 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605136944"></a>2.4. ao_dma_start</h3></div></div></div><pre class="programlisting">
551 ao_dma_start(uint8_t id);
553 Arm the specified DMA engine and await a signal from either
554 hardware or software to start transferring data.
555 'ao_dma_done[index]' is cleared when the DMA is started, and
556 then receives the AO_DMA_DONE bit on a successful transfer
557 or the AO_DMA_ABORTED bit if ao_dma_abort was called. Note
558 that it is possible to get both bits if the transfer was
559 aborted after it had finished.
560 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605134640"></a>2.5. ao_dma_done_transfer</h3></div></div></div><pre class="programlisting">
562 ao_dma_done_transfer(uint8_t id);
564 Signals that a specific DMA engine is done being used. This
565 allows multiple drivers to use the same DMA engine safely.
566 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605132656"></a>2.6. ao_dma_abort</h3></div></div></div><pre class="programlisting">
568 ao_dma_abort(uint8_t id)
570 Terminate any in-progress DMA transaction, marking its
571 'done' variable with the AO_DMA_ABORTED bit.
572 </p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idm46302605130384"></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="#idm46302605128880">1. putchar</a></span></dt><dt><span class="section"><a href="#idm46302605126912">2. getchar</a></span></dt><dt><span class="section"><a href="#idm46302605124816">3. flush</a></span></dt><dt><span class="section"><a href="#idm46302605122784">4. ao_add_stdio</a></span></dt></dl></div><p>
573 AltOS offers a stdio interface over USB, serial and the RF
574 packet link. This provides for control of the device locally or
575 remotely. This is hooked up to the stdio functions by providing
576 the standard putchar/getchar/flush functions. These
577 automatically multiplex the available communication channels;
578 output is always delivered to the channel which provided the
580 </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605128880"></a>1. putchar</h2></div></div></div><pre class="programlisting">
584 Delivers a single character to the current console
586 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605126912"></a>2. getchar</h2></div></div></div><pre class="programlisting">
590 Reads a single character from any of the available
591 console devices. The current console device is set to
592 that which delivered this character. This blocks until
593 a character is available.
594 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605124816"></a>3. flush</h2></div></div></div><pre class="programlisting">
598 Flushes the current console device output buffer. Any
599 pending characters will be delivered to the target device.
600 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605122784"></a>4. ao_add_stdio</h2></div></div></div><pre class="programlisting">
602 ao_add_stdio(char (*pollchar)(void),
603 void (*putchar)(char),
606 This adds another console device to the available
609 'pollchar' returns either an available character or
610 AO_READ_AGAIN if none is available. Significantly, it does
611 not block. The device driver must set 'ao_stdin_ready' to
612 1 and call ao_wakeup(&ao_stdin_ready) when it receives
613 input to tell getchar that more data is available, at
614 which point 'pollchar' will be called again.
616 'putchar' queues a character for output, flushing if the output buffer is
617 full. It may block in this case.
619 'flush' forces the output buffer to be flushed. It may
620 block until the buffer is delivered, but it is not
622 </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idm46302605118752"></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="#idm46302605117344">1. ao_cmd_register</a></span></dt><dt><span class="section"><a href="#idm46302605108608">2. ao_cmd_lex</a></span></dt><dt><span class="section"><a href="#idm46302605106528">3. ao_cmd_put16</a></span></dt><dt><span class="section"><a href="#idm46302605104688">4. ao_cmd_put8</a></span></dt><dt><span class="section"><a href="#idm46302605102800">5. ao_cmd_white</a></span></dt><dt><span class="section"><a href="#idm46302605100736">6. ao_cmd_hex</a></span></dt><dt><span class="section"><a href="#idm46302605098688">7. ao_cmd_decimal</a></span></dt><dt><span class="section"><a href="#idm46302605096592">8. ao_match_word</a></span></dt><dt><span class="section"><a href="#idm46302605094512">9. ao_cmd_init</a></span></dt></dl></div><p>
623 AltOS includes a simple command line parser which is hooked up
624 to the stdio interfaces permitting remote control of the device
625 over USB, serial or the RF link as desired. Each command uses a
626 single character to invoke it, the remaining characters on the
627 line are available as parameters to the command.
628 </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605117344"></a>1. ao_cmd_register</h2></div></div></div><pre class="programlisting">
630 ao_cmd_register(__code struct ao_cmds *cmds)
632 This registers a set of commands with the command
633 parser. There is a fixed limit on the number of command
634 sets, the system will panic if too many are registered.
635 Each command is defined by a struct ao_cmds entry:
636 </p><pre class="programlisting">
643 'cmd' is the character naming the command. 'func' is the
644 function to invoke and 'help' is a string displayed by the
645 '?' command. Syntax errors found while executing 'func'
646 should be indicated by modifying the global ao_cmd_status
647 variable with one of the following values:
648 </p><div class="variablelist"><dl class="variablelist"><dt><span class="term">ao_cmd_success</span></dt><dd><p>
649 The command was parsed successfully. There is no
650 need to assign this value, it is the default.
651 </p></dd><dt><span class="term">ao_cmd_lex_error</span></dt><dd><p>
652 A token in the line was invalid, such as a number
653 containing invalid characters. The low-level
654 lexing functions already assign this value as needed.
655 </p></dd><dt><span class="term">ao_syntax_error</span></dt><dd><p>
656 The command line is invalid for some reason other
658 </p></dd></dl></div><p>
659 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605108608"></a>2. ao_cmd_lex</h2></div></div></div><pre class="programlisting">
663 This gets the next character out of the command line
664 buffer and sticks it into ao_cmd_lex_c. At the end of the
665 line, ao_cmd_lex_c will get a newline ('\n') character.
666 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605106528"></a>3. ao_cmd_put16</h2></div></div></div><pre class="programlisting">
668 ao_cmd_put16(uint16_t v);
670 Writes 'v' as four hexadecimal characters.
671 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605104688"></a>4. ao_cmd_put8</h2></div></div></div><pre class="programlisting">
673 ao_cmd_put8(uint8_t v);
675 Writes 'v' as two hexadecimal characters.
676 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605102800"></a>5. ao_cmd_white</h2></div></div></div><pre class="programlisting">
680 This skips whitespace by calling ao_cmd_lex while
681 ao_cmd_lex_c is either a space or tab. It does not skip
682 any characters if ao_cmd_lex_c already non-white.
683 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605100736"></a>6. ao_cmd_hex</h2></div></div></div><pre class="programlisting">
687 This reads a 16-bit hexadecimal value from the command
688 line with optional leading whitespace. The resulting value
689 is stored in ao_cmd_lex_i;
690 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605098688"></a>7. ao_cmd_decimal</h2></div></div></div><pre class="programlisting">
694 This reads a 32-bit decimal value from the command
695 line with optional leading whitespace. The resulting value
696 is stored in ao_cmd_lex_u32 and the low 16 bits are stored
698 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605096592"></a>8. ao_match_word</h2></div></div></div><pre class="programlisting">
700 ao_match_word(__code char *word)
702 This checks to make sure that 'word' occurs on the command
703 line. It does not skip leading white space. If 'word' is
704 found, then 1 is returned. Otherwise, ao_cmd_status is set to
705 ao_cmd_syntax_error and 0 is returned.
706 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605094512"></a>9. ao_cmd_init</h2></div></div></div><pre class="programlisting">
710 Initializes the command system, setting up the built-in
711 commands and adding a task to run the command processing
712 loop. It should be called by 'main' before ao_start_scheduler.
713 </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idm46302605092288"></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="#idm46302605090064">1. ao_usb_flush</a></span></dt><dt><span class="section"><a href="#idm46302605087920">2. ao_usb_putchar</a></span></dt><dt><span class="section"><a href="#idm46302605085744">3. ao_usb_pollchar</a></span></dt><dt><span class="section"><a href="#idm46302605083600">4. ao_usb_getchar</a></span></dt><dt><span class="section"><a href="#idm46302605081584">5. ao_usb_disable</a></span></dt><dt><span class="section"><a href="#idm46302605078752">6. ao_usb_enable</a></span></dt><dt><span class="section"><a href="#idm46302605076640">7. ao_usb_init</a></span></dt></dl></div><p>
714 AltOS contains a full-speed USB target device driver. It can be
715 programmed to offer any kind of USB target, but to simplify
716 interactions with a variety of operating systems, AltOS provides
717 only a single target device profile, that of a USB modem which
718 has native drivers for Linux, Windows and Mac OS X. It would be
719 easy to change the code to provide an alternate target device if
722 To the rest of the system, the USB device looks like a simple
723 two-way byte stream. It can be hooked into the command line
724 interface if desired, offering control of the device over the
725 USB link. Alternatively, the functions can be accessed directly
726 to provide for USB-specific I/O.
727 </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605090064"></a>1. ao_usb_flush</h2></div></div></div><pre class="programlisting">
731 Flushes any pending USB output. This queues an 'IN' packet
732 to be delivered to the USB host if there is pending data,
733 or if the last IN packet was full to indicate to the host
734 that there isn't any more pending data available.
735 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605087920"></a>2. ao_usb_putchar</h2></div></div></div><pre class="programlisting">
737 ao_usb_putchar(char c);
739 If there is a pending 'IN' packet awaiting delivery to the
740 host, this blocks until that has been fetched. Then, this
741 adds a byte to the pending IN packet for delivery to the
742 USB host. If the USB packet is full, this queues the 'IN'
744 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605085744"></a>3. ao_usb_pollchar</h2></div></div></div><pre class="programlisting">
746 ao_usb_pollchar(void);
748 If there are no characters remaining in the last 'OUT'
749 packet received, this returns AO_READ_AGAIN. Otherwise, it
750 returns the next character, reporting to the host that it
751 is ready for more data when the last character is gone.
752 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605083600"></a>4. ao_usb_getchar</h2></div></div></div><pre class="programlisting">
754 ao_usb_getchar(void);
756 This uses ao_pollchar to receive the next character,
757 blocking while ao_pollchar returns AO_READ_AGAIN.
758 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605081584"></a>5. ao_usb_disable</h2></div></div></div><pre class="programlisting">
760 ao_usb_disable(void);
762 This turns off the USB controller. It will no longer
763 respond to host requests, nor return characters. Calling
764 any of the i/o routines while the USB device is disabled
765 is undefined, and likely to break things. Disabling the
766 USB device when not needed saves power.
768 Note that neither TeleDongle nor TeleMetrum are able to
769 signal to the USB host that they have disconnected, so
770 after disabling the USB device, it's likely that the cable
771 will need to be disconnected and reconnected before it
773 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605078752"></a>6. ao_usb_enable</h2></div></div></div><pre class="programlisting">
777 This turns the USB controller on again after it has been
778 disabled. See the note above about needing to physically
779 remove and re-insert the cable to get the host to
780 re-initialize the USB link.
781 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605076640"></a>7. ao_usb_init</h2></div></div></div><pre class="programlisting">
785 This turns the USB controller on, adds a task to handle
786 the control end point and adds the usb I/O functions to
787 the stdio system. Call this from main before
789 </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idm46302605074416"></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="#idm46302605072400">1. ao_serial_getchar</a></span></dt><dt><span class="section"><a href="#idm46302605070368">2. ao_serial_putchar</a></span></dt><dt><span class="section"><a href="#idm46302605068400">3. ao_serial_drain</a></span></dt><dt><span class="section"><a href="#idm46302605066400">4. ao_serial_set_speed</a></span></dt><dt><span class="section"><a href="#idm46302605064368">5. ao_serial_init</a></span></dt></dl></div><p>
790 The CC1111 provides two USART peripherals. AltOS uses one for
791 asynch serial data, generally to communicate with a GPS device,
792 and the other for a SPI bus. The UART is configured to operate
793 in 8-bits, no parity, 1 stop bit framing. The default
794 configuration has clock settings for 4800, 9600 and 57600 baud
795 operation. Additional speeds can be added by computing
796 appropriate clock values.
798 To prevent loss of data, AltOS provides receive and transmit
799 fifos of 32 characters each.
800 </p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605072400"></a>1. ao_serial_getchar</h2></div></div></div><pre class="programlisting">
802 ao_serial_getchar(void);
804 Returns the next character from the receive fifo, blocking
805 until a character is received if the fifo is empty.
806 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605070368"></a>2. ao_serial_putchar</h2></div></div></div><pre class="programlisting">
808 ao_serial_putchar(char c);
810 Adds a character to the transmit fifo, blocking if the
811 fifo is full. Starts transmitting characters.
812 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605068400"></a>3. ao_serial_drain</h2></div></div></div><pre class="programlisting">
814 ao_serial_drain(void);
816 Blocks until the transmit fifo is empty. Used internally
817 when changing serial speeds.
818 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605066400"></a>4. ao_serial_set_speed</h2></div></div></div><pre class="programlisting">
820 ao_serial_set_speed(uint8_t speed);
822 Changes the serial baud rate to one of
823 AO_SERIAL_SPEED_4800, AO_SERIAL_SPEED_9600 or
824 AO_SERIAL_SPEED_57600. This first flushes the transmit
825 fifo using ao_serial_drain.
826 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605064368"></a>5. ao_serial_init</h2></div></div></div><pre class="programlisting">
830 Initializes the serial peripheral. Call this from 'main'
831 before jumping to ao_start_scheduler. The default speed
832 setting is AO_SERIAL_SPEED_4800.
833 </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="idm46302605062176"></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="#idm46302605061504">1. Radio Introduction</a></span></dt><dt><span class="section"><a href="#idm46302605054896">2. ao_radio_set_telemetry</a></span></dt><dt><span class="section"><a href="#idm46302605052800">3. ao_radio_set_packet</a></span></dt><dt><span class="section"><a href="#idm46302605050704">4. ao_radio_set_rdf</a></span></dt><dt><span class="section"><a href="#idm46302605048576">5. ao_radio_idle</a></span></dt><dt><span class="section"><a href="#idm46302605046640">6. ao_radio_get</a></span></dt><dt><span class="section"><a href="#idm46302605044720">7. ao_radio_put</a></span></dt><dt><span class="section"><a href="#idm46302605042944">8. ao_radio_abort</a></span></dt><dt><span class="section"><a href="#idm46302605040992">9. Radio Telemetry</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605039728">9.1. ao_radio_send</a></span></dt><dt><span class="section"><a href="#idm46302605037568">9.2. ao_radio_recv</a></span></dt></dl></dd><dt><span class="section"><a href="#idm46302605035168">10. Radio Direction Finding</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605034048">10.1. ao_radio_rdf</a></span></dt></dl></dd><dt><span class="section"><a href="#idm46302605032016">11. Radio Packet Mode</a></span></dt><dd><dl><dt><span class="section"><a href="#idm46302605030736">11.1. ao_packet_putchar</a></span></dt><dt><span class="section"><a href="#idm46302605028624">11.2. ao_packet_pollchar</a></span></dt><dt><span class="section"><a href="#idm46302605026656">11.3. ao_packet_slave_start</a></span></dt><dt><span class="section"><a href="#idm46302605024768">11.4. ao_packet_slave_stop</a></span></dt><dt><span class="section"><a href="#idm46302605022912">11.5. ao_packet_slave_init</a></span></dt><dt><span class="section"><a href="#idm46302605020944">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="idm46302605061504"></a>1. Radio Introduction</h2></div></div></div><p>
834 The CC1111 radio transceiver sends and receives digital packets
835 with forward error correction and detection. The AltOS driver is
836 fairly specific to the needs of the TeleMetrum and TeleDongle
837 devices, using it for other tasks may require customization of
838 the driver itself. There are three basic modes of operation:
839 </p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
840 Telemetry mode. In this mode, TeleMetrum transmits telemetry
841 frames at a fixed rate. The frames are of fixed size. This
842 is strictly a one-way communication from TeleMetrum to
844 </p></li><li class="listitem"><p>
845 Packet mode. In this mode, the radio is used to create a
846 reliable duplex byte stream between TeleDongle and
847 TeleMetrum. This is an asymmetrical protocol with
848 TeleMetrum only transmitting in response to a packet sent
849 from TeleDongle. Thus getting data from TeleMetrum to
850 TeleDongle requires polling. The polling rate is adaptive,
851 when no data has been received for a while, the rate slows
852 down. The packets are checked at both ends and invalid
855 On the TeleMetrum side, the packet link is hooked into the
856 stdio mechanism, providing an alternate data path for the
857 command processor. It is enabled when the unit boots up in
860 On the TeleDongle side, the packet link is enabled with a
861 command; data from the stdio package is forwarded over the
862 packet link providing a connection from the USB command
863 stream to the remote TeleMetrum device.
864 </p></li><li class="listitem"><p>
865 Radio Direction Finding mode. In this mode, TeleMetrum
866 constructs a special packet that sounds like an audio tone
867 when received by a conventional narrow-band FM
868 receiver. This is designed to provide a beacon to track
869 the device when other location mechanisms fail.
870 </p></li></ol></div><p>
871 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605054896"></a>2. ao_radio_set_telemetry</h2></div></div></div><pre class="programlisting">
873 ao_radio_set_telemetry(void);
875 Configures the radio to send or receive telemetry
876 packets. This includes packet length, modulation scheme and
877 other RF parameters. It does not include the base frequency
878 or channel though. Those are set at the time of transmission
879 or reception, in case the values are changed by the user.
880 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605052800"></a>3. ao_radio_set_packet</h2></div></div></div><pre class="programlisting">
882 ao_radio_set_packet(void);
884 Configures the radio to send or receive packet data. This
885 includes packet length, modulation scheme and other RF
886 parameters. It does not include the base frequency or
887 channel though. Those are set at the time of transmission or
888 reception, in case the values are changed by the user.
889 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605050704"></a>4. ao_radio_set_rdf</h2></div></div></div><pre class="programlisting">
891 ao_radio_set_rdf(void);
893 Configures the radio to send RDF 'packets'. An RDF 'packet'
894 is a sequence of hex 0x55 bytes sent at a base bit rate of
895 2kbps using a 5kHz deviation. All of the error correction
896 and data whitening logic is turned off so that the resulting
897 modulation is received as a 1kHz tone by a conventional 70cm
899 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605048576"></a>5. ao_radio_idle</h2></div></div></div><pre class="programlisting">
903 Sets the radio device to idle mode, waiting until it reaches
904 that state. This will terminate any in-progress transmit or
906 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605046640"></a>6. ao_radio_get</h2></div></div></div><pre class="programlisting">
910 Acquires the radio mutex and then configures the radio
911 frequency using the global radio calibration and channel
913 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605044720"></a>7. ao_radio_put</h2></div></div></div><pre class="programlisting">
917 Releases the radio mutex.
918 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605042944"></a>8. ao_radio_abort</h2></div></div></div><pre class="programlisting">
920 ao_radio_abort(void);
922 Aborts any transmission or reception process by aborting the
923 associated DMA object and calling ao_radio_idle to terminate
925 </p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605040992"></a>9. Radio Telemetry</h2></div></div></div><p>
926 In telemetry mode, you can send or receive a telemetry
927 packet. The data from receiving a packet also includes the RSSI
928 and status values supplied by the receiver. These are added
929 after the telemetry data.
930 </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605039728"></a>9.1. ao_radio_send</h3></div></div></div><pre class="programlisting">
932 ao_radio_send(__xdata struct ao_telemetry *telemetry);
934 This sends the specific telemetry packet, waiting for the
935 transmission to complete. The radio must have been set to
936 telemetry mode. This function calls ao_radio_get() before
937 sending, and ao_radio_put() afterwards, to correctly
938 serialize access to the radio device.
939 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605037568"></a>9.2. ao_radio_recv</h3></div></div></div><pre class="programlisting">
941 ao_radio_recv(__xdata struct ao_radio_recv *radio);
943 This blocks waiting for a telemetry packet to be received.
944 The radio must have been set to telemetry mode. This
945 function calls ao_radio_get() before receiving, and
946 ao_radio_put() afterwards, to correctly serialize access
947 to the radio device. This returns non-zero if a packet was
948 received, or zero if the operation was aborted (from some
949 other task calling ao_radio_abort()).
950 </p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605035168"></a>10. Radio Direction Finding</h2></div></div></div><p>
951 In radio direction finding mode, there's just one function to
953 </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605034048"></a>10.1. ao_radio_rdf</h3></div></div></div><pre class="programlisting">
955 ao_radio_rdf(int ms);
957 This sends an RDF packet lasting for the specified amount
958 of time. The maximum length is 1020 ms.
959 </p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idm46302605032016"></a>11. Radio Packet Mode</h2></div></div></div><p>
960 Packet mode is asymmetrical and is configured at compile time
961 for either master or slave mode (but not both). The basic I/O
962 functions look the same at both ends, but the internals are
963 different, along with the initialization steps.
964 </p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605030736"></a>11.1. ao_packet_putchar</h3></div></div></div><pre class="programlisting">
966 ao_packet_putchar(char c);
968 If the output queue is full, this first blocks waiting for
969 that data to be delivered. Then, queues a character for
970 packet transmission. On the master side, this will
971 transmit a packet if the output buffer is full. On the
972 slave side, any pending data will be sent the next time
973 the master polls for data.
974 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605028624"></a>11.2. ao_packet_pollchar</h3></div></div></div><pre class="programlisting">
976 ao_packet_pollchar(void);
978 This returns a pending input character if available,
979 otherwise returns AO_READ_AGAIN. On the master side, if
980 this empties the buffer, it triggers a poll for more data.
981 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605026656"></a>11.3. ao_packet_slave_start</h3></div></div></div><pre class="programlisting">
983 ao_packet_slave_start(void);
985 This is available only on the slave side and starts a task
986 to listen for packet data.
987 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605024768"></a>11.4. ao_packet_slave_stop</h3></div></div></div><pre class="programlisting">
989 ao_packet_slave_stop(void);
991 Disables the packet slave task, stopping the radio receiver.
992 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605022912"></a>11.5. ao_packet_slave_init</h3></div></div></div><pre class="programlisting">
994 ao_packet_slave_init(void);
996 Adds the packet stdio functions to the stdio package so
997 that when packet slave mode is enabled, characters will
998 get send and received through the stdio functions.
999 </p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm46302605020944"></a>11.6. ao_packet_master_init</h3></div></div></div><pre class="programlisting">
1001 ao_packet_master_init(void);
1003 Adds the 'p' packet forward command to start packet mode.
1004 </p></div></div></div></div></body></html>