diff options
Diffstat (limited to 'doc/altos.xsl')
| -rw-r--r-- | doc/altos.xsl | 1441 | 
1 files changed, 1441 insertions, 0 deletions
| diff --git a/doc/altos.xsl b/doc/altos.xsl new file mode 100644 index 00000000..9a88a5b5 --- /dev/null +++ b/doc/altos.xsl @@ -0,0 +1,1441 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" +  "/usr/share/xml/docbook/schema/dtd/4.5/docbookx.dtd"> + +<book> +  <title>AltOS</title> +  <subtitle>Altos Metrum Operating System</subtitle> +  <bookinfo> +    <author> +      <firstname>Keith</firstname> +      <surname>Packard</surname> +    </author> +    <copyright> +      <year>2010</year> +      <holder>Keith Packard</holder> +    </copyright> +    <legalnotice> +      <para> +        This document is released under the terms of the +        <ulink url="http://creativecommons.org/licenses/by-sa/3.0/"> +          Creative Commons ShareAlike 3.0 +        </ulink> +        license. +      </para> +    </legalnotice> +    <revhistory> +      <revision> +        <revnumber>0.1</revnumber> +        <date>22 November 2010</date> +        <revremark>Initial content</revremark> +      </revision> +    </revhistory> +  </bookinfo> +  <chapter> +    <title>Overview</title> +    <para> +      AltOS is a operating system built for the 8051-compatible +      processor found in the TI cc1111 microcontroller. It's designed +      to be small and easy to program with. The main features are: +    <itemizedlist> +      <listitem> +	<para>Multi-tasking. While the 8051 doesn'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. +	</para> +      </listitem> +      <listitem> +	<para>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. +	</para> +      </listitem> +      <listitem> +	<para>Sleep/wakeup scheduling. Taken directly from ancient +	Unix designs, these two provide the fundemental scheduling +	primitive within AltOS. +	</para> +      </listitem> +      <listitem> +	<para>Mutexes. As a locking primitive, mutexes are easier to +	use than semaphores, at least in my experience. +	</para> +      </listitem> +      <listitem> +	<para>Timers. Tasks can set an alarm which will abort any +	pending sleep, allowing operations to time-out instead of +	blocking forever. +	</para> +      </listitem> +    </itemizedlist> +    </para> +    <para> +      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: +      <programlisting> +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(); +} +      </programlisting> +      As you can see, a long sequence of subsystems are initialized +      and then the scheduler is started. +    </para> +  </chapter> +  <chapter> +    <title>Programming the 8051 with SDCC</title> +    <para> +      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. +    </para> +    <section> +      <title>8051 memory spaces</title> +      <para> +	The __data/__xdata/__code memory spaces below were completely +	separate in the original 8051 design. In the cc1111, this +	isn't true—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. +      </para> +      <variablelist> +	<title>SDCC 8051 memory spaces</title> +	<varlistentry> +	  <term>__data</term> +	  <listitem> +	    <para> +	      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. +	    </para> +	  </listitem> +	</varlistentry> +	<varlistentry> +	  <term>__idata</term> +	  <listitem> +	    <para> +	      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. +	    </para> +	  </listitem> +	</varlistentry> +	<varlistentry> +	  <term>__xdata</term> +	  <listitem> +	    <para> +	      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. +	    </para> +	  </listitem> +	</varlistentry> +	<varlistentry> +	  <term>__pdata</term> +	  <listitem> +	    <para> +	      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. +	    </para> +	  </listitem> +	</varlistentry> +	<varlistentry> +	  <term>__code</term> +	  <listitem> +	    <para> +	      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. +	    </para> +	  </listitem> +	</varlistentry> +	<varlistentry> +	  <term>__bit</term> +	  <listitem> +	    <para> +	      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. +	    </para> +	  </listitem> +	</varlistentry> +	<varlistentry> +	  <term>__sfr, __sfr16, __sfr32, __sbit</term> +	  <listitem> +	    <para> +	      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. +	    </para> +	  </listitem> +	</varlistentry> +      </variablelist> +    </section> +    <section> +      <title>Function calls on the 8051</title> +      <para> +	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. +      </para> +      <section> +	<title>__reentrant functions</title> +	<para> +	  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. +	</para> +	<para> +	  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. +	</para> +      </section> +      <section> +	<title>Non __reentrant functions</title> +	<para> +	  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. +	</para> +	<para> +	  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. +	</para> +      </section> +      <section> +	<title>__interrupt functions</title> +	<para> +	  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. +	</para> +      </section> +      <section> +	<title>__critical functions and statements</title> +	<para> +	  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. +	</para> +      </section> +    </section> +  </chapter> +  <chapter> +    <title>Task functions</title> +    <para> +      This chapter documents how to create, destroy and schedule AltOS tasks. +    </para> +    <variablelist> +      <title>AltOS Task Functions</title> +      <varlistentry> +	<term>ao_add_task</term> +	<listitem> +	  <programlisting> +void +ao_add_task(__xdata struct ao_task * task, +            void (*start)(void), +            __code char *name); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_exit</term> +	<listitem> +	  <programlisting> +void +ao_exit(void) +	  </programlisting> +	  <para> +	    This terminates the current task. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_sleep</term> +	<listitem> +	  <programlisting> +void +ao_sleep(__xdata void *wchan) +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_wakeup</term> +	<listitem> +	  <programlisting> +void +ao_wakeup(__xdata void *wchan) +	  </programlisting> +	  <para> +	    Wake all tasks blocked on 'wchan'. This makes them +	    available to be run again, but does not actually switch +	    to another task. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_alarm</term> +	<listitem> +	  <programlisting> +void +ao_alarm(uint16_t delay) +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_wake_task</term> +	<listitem> +	  <programlisting> +void +ao_wake_task(__xdata struct ao_task *task) +	  </programlisting> +	  <para> +	    Force a specific task to wake up, independent of which +	    'wchan' it is waiting for. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_start_scheduler</term> +	<listitem> +	  <programlisting> +void +ao_start_scheduler(void) +	  </programlisting> +	  <para> +	    This is called from 'main' when the system is all +	    initialized and ready to run. It will not return. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_clock_init</term> +	<listitem> +	  <programlisting> +void +ao_clock_init(void) +	  </programlisting> +	  <para> +	    This turns on the external 48MHz clock then switches the +	    hardware to using it. This is required by many of the +	    internal devices like USB. It should be called by the +	    'main' function first, before initializing any of the +	    other devices in the system. +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +  </chapter> +  <chapter> +    <title>Timer Functions</title> +    <para> +      AltOS sets up one of the cc1111 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 ADC system to +      collect current data readings. Doing this from the ISR ensures +      that the ADC values are sampled at a regular rate, independent +      of any scheduling jitter. +    </para> +    <variablelist> +      <title>AltOS Timer Functions</title> +      <varlistentry> +	<term>ao_time</term> +	<listitem> +	  <programlisting> +uint16_t +ao_time(void) +	  </programlisting> +	  <para> +	    Returns the current system tick count. Note that this is +	    only a 16 bit value, and so it wraps every 655.36 seconds. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_delay</term> +	<listitem> +	  <programlisting> +void +ao_delay(uint16_t ticks); +	  </programlisting> +	  <para> +	    Suspend the current task for at least 'ticks' clock units. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_timer_set_adc_interval</term> +	<listitem> +	  <programlisting> +void +ao_timer_set_adc_interval(uint8_t interval); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_timer_init</term> +	<listitem> +	  <programlisting> +void +ao_timer_init(void) +	  </programlisting> +	  <para> +	    This turns on the 100Hz tick using the CC1111 timer 1. It +	    is required for any of the time-based functions to +	    work. It should be called by 'main' before ao_start_scheduler. +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +  </chapter> +  <chapter> +    <title>AltOS Mutexes</title> +    <para> +      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. +    </para> +    <variablelist> +      <title>Mutex Functions</title> +      <varlistentry> +	<term>ao_mutex_get</term> +	<listitem> +	  <programlisting> +void +ao_mutex_get(__xdata uint8_t *mutex); +	  </programlisting> +	  <para> +	    Acquires the specified mutex, blocking if the mutex is +	    owned by another task. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_mutex_put</term> +	<listitem> +	  <programlisting> +void +ao_mutex_put(__xdata uint8_t *mutex); +	  </programlisting> +	  <para> +	    Releases the specified mutex, waking up all tasks waiting +	    for it. +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +  </chapter> +  <chapter> +    <title>CC1111 DMA engine</title> +    <para> +      The CC1111 contains a useful bit of extra hardware in the form +      of five 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 radio and SPI data. +    </para> +    <para> +      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. +    </para> +    <para> +      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. +    </para> +    <variablelist> +      <title>AltOS DMA functions</title> +      <varlistentry> +	<term>ao_dma_alloc</term> +	<listitem> +	  <programlisting> +uint8_t +ao_dma_alloc(__xdata uint8_t *done) +	  </programlisting> +	  <para> +	    Allocates a DMA engine, returning the identifier. Whenever +	    this DMA engine completes a transfer. '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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_dma_set_transfer</term> +	<listitem> +	  <programlisting> +void +ao_dma_set_transfer(uint8_t id, +		    void __xdata *srcaddr, +		    void __xdata *dstaddr, +		    uint16_t count, +		    uint8_t cfg0, +		    uint8_t cfg1) +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_dma_start</term> +	<listitem> +	  <programlisting> +void +ao_dma_start(uint8_t id); +	  </programlisting> +	  <para> +	    Arm the specified DMA engine and await a signal from +	    either hardware or software to start transferring data. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_dma_trigger</term> +	<listitem> +	  <programlisting> +void +ao_dma_trigger(uint8_t id) +	  </programlisting> +	  <para> +	    Trigger the specified DMA engine to start copying data. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_dma_abort</term> +	<listitem> +	  <programlisting> +void +ao_dma_abort(uint8_t id) +	  </programlisting> +	  <para> +	    Terminate any in-progress DMA transation, marking its +	    'done' variable with the AO_DMA_ABORTED bit. +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +  </chapter> +  <chapter> +    <title>SDCC Stdio interface</title> +    <para> +      AltOS offers a stdio interface over both USB and the RF packet +      link. This provides for control of the device localy or +      remotely. This is hooked up to the stdio functions in SDCC by +      providing the standard putchar/getchar/flush functions. These +      automatically multiplex the two available communication +      channels; output is always delivered to the channel which +      provided the most recent input. +    </para> +    <variablelist> +      <title>SDCC stdio functions</title> +      <varlistentry> +	<term>putchar</term> +	<listitem> +	  <programlisting> +void +putchar(char c) +	  </programlisting> +	  <para> +	    Delivers a single character to the current console +	    device. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>getchar</term> +	<listitem> +	  <programlisting> +char +getchar(void) +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>flush</term> +	<listitem> +	  <programlisting> +void +flush(void) +	  </programlisting> +	  <para> +	    Flushes the current console device output buffer. Any +	    pending characters will be delivered to the target device. +xo	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_add_stdio</term> +	<listitem> +	  <programlisting> +void +ao_add_stdio(char (*pollchar)(void), +	     void (*putchar)(char), +	     void (*flush)(void)) +	  </programlisting> +	  <para> +	    This adds another console device to the available +	    list. +	  </para> +	  <para> +	    '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(&ao_stdin_ready) when it receives +	    input to tell getchar that more data is available, at +	    which point 'pollchar' will be called again. +	  </para> +	  <para> +	    'putchar' queues a character for output, flushing if the output buffer is +	    full. It may block in this case. +	  </para> +	  <para> +	    'flush' forces the output buffer to be flushed. It may +	    block until the buffer is delivered, but it is not +	    required to do so. +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +  </chapter> +  <chapter> +    <title>Command line interface</title> +    <para> +      AltOS includes a simple command line parser which is hooked up +      to the stdio interfaces permitting remote control of the device +      over USB 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. +    </para> +    <variablelist> +      <title>AltOS command line parsing functions</title> +      <varlistentry> +	<term>ao_cmd_register</term> +	<listitem> +	  <programlisting> +void +ao_cmd_register(__code struct ao_cmds *cmds) +	  </programlisting> +	  <para> +	    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: +	    <programlisting> +struct ao_cmds { +	char		cmd; +	void		(*func)(void); +	const char	*help; +}; +	    </programlisting> +	    '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: +	    <variablelist> +	      <varlistentry> +		<term>ao_cmd_success</term> +		<listitem> +		  <para> +		    The command was parsed successfully. There is no +		    need to assign this value, it is the default. +		  </para> +		</listitem> +	      </varlistentry> +	      <varlistentry> +		<term>ao_cmd_lex_error</term> +		<listitem> +		  <para> +		    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. +		  </para> +		</listitem> +	      </varlistentry> +	      <varlistentry> +		<term>ao_syntax_error</term> +		<listitem> +		  <para> +		    The command line is invalid for some reason other +		    than invalid tokens. +		  </para> +		</listitem> +	      </varlistentry> +	    </variablelist> +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_cmd_lex</term> +	<listitem> +	  <programlisting> +void +ao_cmd_lex(void); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_cmd_put16</term> +	<listitem> +	  <programlisting> +void +ao_cmd_put16(uint16_t v); +	  </programlisting> +	  <para> +	    Writes 'v' as four hexadecimal characters. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_cmd_put8</term> +	<listitem> +	  <programlisting> +void +ao_cmd_put8(uint8_t v); +	  </programlisting> +	  <para> +	    Writes 'v' as two hexadecimal characters. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_cmd_white</term> +	<listitem> +	  <programlisting> +void +ao_cmd_white(void) +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_cmd_hex</term> +	<listitem> +	  <programlisting> +void +ao_cmd_hex(void) +	  </programlisting> +	  <para> +	    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; +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_cmd_decimal</term> +	<listitem> +	  <programlisting> +void +ao_cmd_decimal(void) +	  </programlisting> +	  <para> +	    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; +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_match_word</term> +	<listitem> +	  <programlisting> +uint8_t +ao_match_word(__code char *word) +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_cmd_init</term> +	<listitem> +	  <programlisting> +void +ao_cmd_init(void +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +  </chapter> +  <chapter> +    <title>CC1111 USB target device</title> +    <para> +      The CC1111 contains a full-speed USB target device. 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. +    </para> +    <para> +      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. +    </para> +    <variablelist> +      <title>AltOS USB functions</title> +      <varlistentry> +	<term>ao_usb_flush</term> +	<listitem> +	  <programlisting> +void +ao_usb_flush(void); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_usb_putchar</term> +	<listitem> +	  <programlisting> +void +ao_usb_putchar(char c); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_usb_pollchar</term> +	<listitem> +	  <programlisting> +char +ao_usb_pollchar(void); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_usb_getchar</term> +	<listitem> +	  <programlisting> +char +ao_usb_getchar(void); +	  </programlisting> +	  <para> +	    This uses ao_pollchar to receive the next character, +	    blocking while ao_pollchar returns AO_READ_AGAIN. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_usb_disable</term> +	<listitem> +	  <programlisting> +void +ao_usb_disable(void); +	  </programlisting> +	  <para> +	    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. +	  </para> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_usb_enable</term> +	<listitem> +	  <programlisting> +void +ao_usb_enable(void); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_usb_init</term> +	<listitem> +	  <programlisting> +void +ao_usb_init(void); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +  </chapter> +  <chapter> +    <title>CC1111 Serial peripheral</title> +    <para> +      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. +    </para> +    <para> +      To prevent loss of data, AltOS provides receive and transmit +      fifos of 32 characters each. +    </para> +    <variablelist> +      <title>AltOS serial functions</title> +      <varlistentry> +	<term>ao_serial_getchar</term> +	<listitem> +	  <programlisting> +char +ao_serial_getchar(void); +	  </programlisting> +	  <para> +	    Returns the next character from the receive fifo, blocking +	    until a character is received if the fifo is empty. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_serial_putchar</term> +	<listitem> +	  <programlisting> +void +ao_serial_putchar(char c); +	  </programlisting> +	  <para> +	    Adds a character to the transmit fifo, blocking if the +	    fifo is full. Starts transmitting characters. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_serial_drain</term> +	<listitem> +	  <programlisting> +void +ao_serial_drain(void); +	  </programlisting> +	  <para> +	    Blocks until the transmit fifo is empty. Used internally +	    when changing serial speeds. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_serial_set_speed</term> +	<listitem> +	  <programlisting> +void +ao_serial_set_speed(uint8_t speed); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_serial_init</term> +	<listitem> +	  <programlisting> +void +ao_serial_init(void) +	  </programlisting> +	  <para> +	    Initializes the serial peripheral. Call this from 'main' +	    before jumping to ao_start_scheduler. The default speed +	    setting is AO_SERIAL_SPEED_4800. +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +  </chapter> +  <chapter> +    <title>CC1111 Radio peripheral</title> +    <para> +      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: +      <orderedlist> +	<listitem> +	  <para> +	    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. +	  </para> +	</listitem> +	<listitem> +	  <para> +	    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. +	  </para> +	  <para> +	    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. +	  </para> +	  <para> +	    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. +	  </para> +	</listitem> +	<listitem> +	  <para> +	    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. +	  </para> +	</listitem> +      </orderedlist> +    </para> +    <variablelist> +      <title>AltOS radio functions</title> +      <varlistentry> +	<term>ao_radio_set_telemetry</term> +	<listitem> +	  <programlisting> +void +ao_radio_set_telemetry(void); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_radio_set_packet</term> +	<listitem> +	  <programlisting> +void +ao_radio_set_packet(void); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_radio_set_rdf</term> +	<listitem> +	  <programlisting> +void +ao_radio_set_rdf(void); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_radio_idle</term> +	<listitem> +	  <programlisting> +void +ao_radio_idle(void); +	  </programlisting> +	  <para> +	    Sets the radio device to idle mode, waiting until it reaches +	    that state. This will terminate any in-progress transmit or +	    receive operation. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_radio_get</term> +	<listitem> +	  <programlisting> +void +ao_radio_get(void); +	  </programlisting> +	  <para> +	    Acquires the radio mutex and then configures the radio +	    frequency using the global radio calibration and channel +	    values. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_radio_put</term> +	<listitem> +	  <programlisting> +void +ao_radio_put(void); +	  </programlisting> +	  <para> +	    Releases the radio mutex. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_radio_abort</term> +	<listitem> +	  <programlisting> +void +ao_radio_abort(void); +	  </programlisting> +	  <para> +	    Aborts any transmission or reception process by aborting the +	    associated DMA object and calling ao_radio_idle to terminate +	    the radio operation. +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +    <variablelist> +      <title>AltOS radio telemetry functions</title> +      <para> +	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. +      </para> +      <varlistentry> +	<term>ao_radio_send</term> +	<listitem> +	  <programlisting> +void +ao_radio_send(__xdata struct ao_telemetry *telemetry); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_radio_recv</term> +	<listitem> +	  <programlisting> +void +ao_radio_recv(__xdata struct ao_radio_recv *radio); +	  </programlisting> +	  <para> +	    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()). +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +    <variablelist> +      <title>AltOS radio direction finding function</title> +      <para> +	In radio direction finding mode, there's just one function to +	use +      </para> +      <varlistentry> +	<term>ao_radio_rdf</term> +	<listitem> +	  <programlisting> +void +ao_radio_rdf(int ms); +	  </programlisting> +	  <para> +	    This sends an RDF packet lasting for the specified amount +	    of time. The maximum length is 1020 ms. +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +    <variablelist> +      <title>Packet mode functions</title> +      <para> +	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. +      </para> +      <varlistentry> +	<term>ao_packet_putchar</term> +	<listitem> +	  <programlisting> +void +ao_packet_putchar(char c); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_packet_pollchar</term> +	<listitem> +	  <programlisting> +char +ao_packet_pollchar(void); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_packet_slave_start</term> +	<listitem> +	  <programlisting> +void +ao_packet_slave_start(void); +	  </programlisting> +	  <para> +	    This is available only on the slave side and starts a task +	    to listen for packet data. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_packet_slave_stop</term> +	<listitem> +	  <programlisting> +void +ao_packet_slave_stop(void); +	  </programlisting> +	  <para> +	    Disables the packet slave task, stopping the radio receiver. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_packet_slave_init</term> +	<listitem> +	  <programlisting> +void +ao_packet_slave_init(void); +	  </programlisting> +	  <para> +	    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. +	  </para> +	</listitem> +      </varlistentry> +      <varlistentry> +	<term>ao_packet_master_init</term> +	<listitem> +	  <programlisting> +void +ao_packet_master_init(void); +	  </programlisting> +	  <para> +	    Adds the 'p' packet forward command to start packet mode. +	  </para> +	</listitem> +      </varlistentry> +    </variablelist> +  </chapter> +</book> | 
