diff options
| -rw-r--r-- | doc/Makefile | 12 | ||||
| -rw-r--r-- | doc/altos-docinfo.xml | 32 | ||||
| -rw-r--r-- | doc/altos.txt | 1400 | ||||
| -rw-r--r-- | doc/altos.xsl | 1612 | 
4 files changed, 1441 insertions, 1615 deletions
diff --git a/doc/Makefile b/doc/Makefile index df1a884c..04402c88 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -134,12 +134,18 @@ SVG=\  RELNOTES_PDF=$(RELNOTES_INC:.inc=.pdf)  RELNOTES_HTML=$(RELNOTES_INC:.inc=.html) +ALTOS_TXT_FILES=\ +	altos.txt + +ALTOS_RAW_FILES=$(ALTOS_TXT_FILES:.txt=.raw) +ALTOS_PDF_FILES=$(ALTOS_TXT_FILES:.txt=.pdf) +  HTML=altusmetrum.html altos.html telemetry.html companion.html micropeak.html telegps.html $(RELNOTES_HTML) -PDF=altusmetrum.pdf $(RELNOTES_PDF) altos.pdf telemetry.pdf companion.pdf micropeak.pdf telegps.pdf \ +PDF=altusmetrum.pdf $(RELNOTES_PDF) $(ALTOS_PDF_FILES) telemetry.pdf companion.pdf micropeak.pdf telegps.pdf \  	$(OUTLINE_PDF_FILES) -FOSTYLE=xorg-fo.xsl +FOSTYLE=am-fo.xsl  TEMPLATES_TMPL=titlepage.templates.tmpl @@ -189,7 +195,7 @@ publish:	$(DOC)  	 git push)  clean: -	rm -f $(HTML) $(PDF) $(TEMPLATES_XSL) +	rm -f $(HTML) $(PDF) $(TEMPLATES_XSL) $(RAW_FILES) $(TELEGPS_RAW_FILES) $(MICROPEAK_RAW_FILES)  distclean: clean  	rm -f $(HTML) $(PDF) diff --git a/doc/altos-docinfo.xml b/doc/altos-docinfo.xml new file mode 100644 index 00000000..1b6ad648 --- /dev/null +++ b/doc/altos-docinfo.xml @@ -0,0 +1,32 @@ +<subtitle>Altos Metrum Operating System</subtitle> +<author> +  <firstname>Keith</firstname> +  <surname>Packard</surname> +  <email>keithp@keithp.com</email> +</author> +<date>05 November 2012</date> +<copyright> +  <year>2012</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>1.1</revnumber> +    <date>05 November 2012</date> +    <revremark>Portable version</revremark> +  </revision> +  <revision> +    <revnumber>0.1</revnumber> +    <date>22 November 2010</date> +    <revremark>Initial content</revremark> +  </revision> +</revhistory> diff --git a/doc/altos.txt b/doc/altos.txt new file mode 100644 index 00000000..f2b1ee59 --- /dev/null +++ b/doc/altos.txt @@ -0,0 +1,1400 @@ += AltOS +:doctype: book +:toc: +:numbered: + +== Overview + +	AltOS is a operating system built for a variety of +	microcontrollers used in Altus Metrum devices. It has a simple +	porting layer for each CPU while providing a convenient +	operating enviroment for the developer. AltOS currently +	supports three different CPUs: + +	* STM32L series from ST Microelectronics. This ARM Cortex-M3 +	  based microcontroller offers low power consumption and a +	  wide variety of built-in peripherals. Altus Metrum uses this +	  in the TeleMega, MegaDongle and TeleLCO projects. + +	* CC1111 from Texas Instruments. This device includes a +	  fabulous 10mW digital RF transceiver along with an +	  8051-compatible processor core and a range of +	  peripherals. This is used in the TeleMetrum, TeleMini, +	  TeleDongle and TeleFire projects which share the need for a +	  small microcontroller and an RF interface. + +	* ATmega32U4 from Atmel. This 8-bit AVR microcontroller is one +	  of the many used to create Arduino boards. The 32U4 includes +	  a USB interface, making it easy to connect to other +	  computers. Altus Metrum used this in prototypes of the +	  TeleScience and TelePyro boards; those have been switched to +	  the STM32L which is more capable and cheaper. + +	Among the features of AltOS are: + +	* Multi-tasking. While microcontrollers often don't +	  provide separate address spaces, it's often easier to write +	  code that operates in separate threads instead of tying +	  everything into one giant event loop. + +	* 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. + +	* Sleep/wakeup scheduling. Taken directly from ancient +	  Unix designs, these two provide the fundemental scheduling +	  primitive within AltOS. + +	* Mutexes. As a locking primitive, mutexes are easier to +	  use than semaphores, at least in my experience. + +	* Timers. Tasks can set an alarm which will abort any +	  pending sleep, allowing operations to time-out instead of +	  blocking forever. + +	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: + +	.... +	\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(); +	\} +	.... + +	As you can see, a long sequence of subsystems are initialized +	and then the scheduler is started. + +== AltOS Porting Layer + +	AltOS provides a CPU-independent interface to various common +	microcontroller subsystems, including GPIO pins, interrupts, +	SPI, I2C, USB and asynchronous serial interfaces. By making +	these CPU-independent, device drivers, generic OS and +	application code can all be written that work on any supported +	CPU. Many of the architecture abstraction interfaces are +	prefixed with ao_arch. + +	=== Low-level CPU operations + +		These primitive operations provide the abstraction needed to +		run the multi-tasking framework while providing reliable +		interrupt delivery. + +		==== ao_arch_block_interrupts/ao_arch_release_interrupts + +			.... +			static inline void +			ao_arch_block_interrupts(void); + +			static inline void +			ao_arch_release_interrupts(void); +			.... + +			These disable/enable interrupt delivery, they may not +			discard any interrupts. Use these for sections of code that +			must be atomic with respect to any code run from an +			interrupt handler. + +		==== ao_arch_save_regs, ao_arch_save_stack, ao_arch_restore_stack + +			.... +			static inline void +			ao_arch_save_regs(void); + +			static inline void +			ao_arch_save_stack(void); + +			static inline void +			ao_arch_restore_stack(void); +			.... + +			These provide all of the support needed to switch +			between tasks.. ao_arch_save_regs must save all CPU +			registers to the current stack, including the +			interrupt enable state. ao_arch_save_stack records the +			current stack location in the current ao_task +			structure. ao_arch_restore_stack switches back to the +			saved stack, restores all registers and branches to +			the saved return address. + +		==== ao_arch_wait_interupt + +			.... +			#define ao_arch_wait_interrupt() +			.... + +			This stops the CPU, leaving clocks and interrupts +			enabled. When an interrupt is received, this must wake up +			and handle the interrupt. ao_arch_wait_interrupt is entered +			with interrupts disabled to ensure that there is no gap +			between determining that no task wants to run and idling the +			CPU. It must sleep the CPU, process interrupts and then +			disable interrupts again. If the CPU doesn't have any +			reduced power mode, this must at the least allow pending +			interrupts to be processed. + +	=== GPIO operations + +	These functions provide an abstract interface to configure and +	manipulate GPIO pins. + +		==== GPIO setup + +			These macros may be invoked at system +			initialization time to configure pins as +			needed for system operation. One tricky aspect +			is that some chips provide direct access to +			specific GPIO pins while others only provide +			access to a whole register full of pins. To +			support this, the GPIO macros provide both +			port+bit and pin arguments. Simply define the +			arguments needed for the target platform and +			leave the others undefined. + +			===== ao_enable_output + +				.... +				#define ao_enable_output(port, bit, pin, value) +				.... + +				Set the specified port+bit (also called 'pin') +				for output, initializing to the specified +				value. The macro must avoid driving the pin +				with the opposite value if at all possible. + +			===== ao_enable_input + +				.... +				#define ao_enable_input(port, bit, mode) +				.... + +				Sets the specified port/bit to be an input +				pin. 'mode' is a combination of one or more of +				the following. Note that some platforms may +				not support the desired mode. In that case, +				the value will not be defined so that the +				program will fail to compile. + +				* AO_EXTI_MODE_PULL_UP. Apply a pull-up to the +				  pin; a disconnected pin will read as 1. + +				* AO_EXTI_MODE_PULL_DOWN. Apply a pull-down to +				  the pin; a disconnected pin will read as 0. + +				* 0. Don't apply either a pull-up or +				  pull-down. A disconnected pin will read an +				  undetermined value. + +		==== Reading and writing GPIO pins + +			These macros read and write individual GPIO pins. + +			===== ao_gpio_set + +				.... +				#define ao_gpio_set(port, bit, pin, value) +				.... + +				Sets the specified port/bit or pin to +				the indicated value + +			===== ao_gpio_get + +				.... +				#define ao_gpio_get(port, bit, pin) +				.... + +				Returns either 1 or 0 depending on +				whether the input to the pin is high +				or low. +== Programming the 8051 with SDCC + +	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. + +	When built on other architectures, the various SDCC-specific +	symbols are #defined as empty strings so they don't affect the +	compiler. + +	=== 8051 memory spaces + +		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. + +		==== __data + +			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. + +		==== __idata + +			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. + +		==== __xdata + +			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. + +		==== __pdata + +			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. + +		==== __code + +			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. + +		==== __bit + +			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. + +		==== __sfr, __sfr16, __sfr32, __sbit + +			Access to physical registers in the device use this mode +			which declares the variable name, its type and the +			address it lives at. No memory is allocated for these +			variables. + +	=== Function calls on the 8051 + +		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. + +		==== __reentrant functions + +			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. + +			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. + +		==== Non __reentrant functions + +			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. + +			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. + +		==== __interrupt functions + +			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. + +		==== __critical functions and statements + +			SDCC has built-in support for suspending interrupts during +			critical code. Functions marked as __critical will have +			interrupts suspended for the whole period of +			execution. Individual statements may also be marked as +			__critical which blocks interrupts during the execution of +			that statement. Keeping critical sections as short as +			possible is key to ensuring that interrupts are handled as +			quickly as possible. AltOS doesn't use this form in shared +			code as other compilers wouldn't know what to do. Use +			ao_arch_block_interrupts and ao_arch_release_interrupts instead. + +== Task functions + +	This chapter documents how to create, destroy and schedule +	AltOS tasks. + +	=== ao_add_task + +		.... +		\void +		\ao_add_task(__xdata struct ao_task * task, +		\	    void (*start)(void), +		\	    __code char *name); +		.... + +		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. + +	=== ao_exit + +		.... +		void +		ao_exit(void) +		.... + +		This terminates the current task. + +	=== ao_sleep + +		.... +		void +		ao_sleep(__xdata void *wchan) +		.... + +		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. + +		Because ao_wakeup wakes every task waiting on a particular +		location, ao_sleep should be used in a loop that first checks +		the desired condition, blocks in ao_sleep and then rechecks +		until the condition is satisfied. If the location may be +		signaled from an interrupt handler, the code will need to +		block interrupts around the block of code. Here's a complete +		example: + +		.... +		\ao_arch_block_interrupts(); +		\while (!ao_radio_done) +		\	ao_sleep(&ao_radio_done); +		\ao_arch_release_interrupts(); +		.... + +	=== ao_wakeup + +		.... +		void +		ao_wakeup(__xdata void *wchan) +		.... + +		Wake all tasks blocked on 'wchan'. This makes them +		available to be run again, but does not actually switch +		to another task. Here's an example of using this: + +		.... +		\if (RFIF & RFIF_IM_DONE) { +		\	ao_radio_done = 1; +		\	ao_wakeup(&ao_radio_done); +		\	RFIF &= ~RFIF_IM_DONE; +		\} +		.... + +		Note that this need not block interrupts as the +		ao_sleep block can only be run from normal mode, and +		so this sequence can never be interrupted with +		execution of the other sequence. + +	=== ao_alarm + +		.... +		void +		ao_alarm(uint16_t delay); + +		void +		ao_clear_alarm(void); +		.... + +		Schedules an alarm to fire in at least 'delay' +		ticks. If the task is asleep when the alarm fires, it +		will wakeup and ao_sleep will return 1. ao_clear_alarm +		resets any pending alarm so that it doesn't fire at +		some arbitrary point in the future. + +		.... +		ao_alarm(ao_packet_master_delay); +		ao_arch_block_interrupts(); +		while (!ao_radio_dma_done) +			if (ao_sleep(&ao_radio_dma_done) != 0) +				ao_radio_abort(); +		ao_arch_release_interrupts(); +		ao_clear_alarm(); +		.... + +		In this example, a timeout is set before waiting for +		incoming radio data. If no data is received before the +		timeout fires, ao_sleep will return 1 and then this +		code will abort the radio receive operation. + +	=== ao_start_scheduler + +		.... +		void +		ao_start_scheduler(void); +		.... + +		This is called from 'main' when the system is all +		initialized and ready to run. It will not return. + +	=== ao_clock_init + +		.... +		void +		ao_clock_init(void); +		.... + +		This initializes the main CPU clock and switches to it. + +== Timer Functions + +	AltOS sets up one of the CPU timers to run at 100Hz and +	exposes this tick as the fundemental unit of time. At each +	interrupt, AltOS increments the counter, and schedules any tasks +	waiting for that time to pass, then fires off the sensors to +	collect current data readings. Doing this from the ISR ensures +	that the values are sampled at a regular rate, independent +	of any scheduling jitter. + +	=== ao_time + +		.... +		uint16_t +		ao_time(void) +		.... + +		Returns the current system tick count. Note that this is +		only a 16 bit value, and so it wraps every 655.36 seconds. + +	=== ao_delay + +		.... +		void +		ao_delay(uint16_t ticks); +		.... + +		Suspend the current task for at least 'ticks' clock units. + +	=== ao_timer_set_adc_interval + +		.... +		void +		ao_timer_set_adc_interval(uint8_t interval); +		.... + +		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. + +	=== ao_timer_init + +		.... +		void +		ao_timer_init(void) +		.... + +		This turns on the 100Hz tick. It is required for any of the +		time-based functions to work. It should be called by 'main' +		before ao_start_scheduler. + +== AltOS Mutexes + +	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. + +	=== ao_mutex_get + +		.... +		void +		ao_mutex_get(__xdata uint8_t *mutex); +		.... + +		Acquires the specified mutex, blocking if the mutex is +		owned by another task. + +	=== ao_mutex_put + +		.... +		void +		ao_mutex_put(__xdata uint8_t *mutex); +		.... + +		Releases the specified mutex, waking up all tasks waiting +		for it. + +== DMA engine + +	The CC1111 and STM32L both contain a useful bit of extra +	hardware in the form of a number of programmable DMA +	engines. They can be configured to copy data in memory, or +	between memory and devices (or even between two devices). AltOS +	exposes a general interface to this hardware and uses it to +	handle both internal and external devices. + +	Because the CC1111 and STM32L DMA engines are different, the +	interface to them is also different. As the DMA engines are +	currently used to implement platform-specific drivers, this +	isn't yet a problem. + +	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. + +	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. + +	=== CC1111 DMA Engine + +		==== ao_dma_alloc + +			.... +			uint8_t +			ao_dma_alloc(__xdata uint8_t *done) +			.... + +			Allocate a DMA engine, returning the +			identifier.  'done' is cleared when the DMA is +			started, and then receives the AO_DMA_DONE bit +			on a successful transfer or the AO_DMA_ABORTED +			bit if ao_dma_abort was called. Note that it +			is possible to get both bits if the transfer +			was aborted after it had finished. + +		==== ao_dma_set_transfer + +			.... +			void +			ao_dma_set_transfer(uint8_t id, +			void __xdata *srcaddr, +			void __xdata *dstaddr, +			uint16_t count, +			uint8_t cfg0, +			uint8_t cfg1) +			.... + +			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. + +		==== ao_dma_start + +			.... +			void +			ao_dma_start(uint8_t id); +			.... + +			Arm the specified DMA engine and await a +			signal from either hardware or software to +			start transferring data. + +		==== ao_dma_trigger + +			.... +			void +			ao_dma_trigger(uint8_t id) +			.... + +			Trigger the specified DMA engine to start +			copying data. + +		==== ao_dma_abort + +			.... +			void +			ao_dma_abort(uint8_t id) +			.... + +			Terminate any in-progress DMA transaction, +			marking its 'done' variable with the +			AO_DMA_ABORTED bit. + +	=== STM32L DMA Engine + +		==== ao_dma_alloc + +			.... +			uint8_t ao_dma_done[]; + +			void +			ao_dma_alloc(uint8_t index); +			.... + +			Reserve a DMA engine for exclusive use by one +			driver. + +		==== ao_dma_set_transfer + +			.... +			void +			ao_dma_set_transfer(uint8_t id, +			void *peripheral, +			void *memory, +			uint16_t count, +			uint32_t ccr); +			.... + +			Initializes the specified dma engine to copy +			data between 'peripheral' and 'memory' for +			'count' units. 'ccr' is a value directly out +			of the STM32L documentation and tells the DMA +			engine what the transfer unit size, direction +			and step are. + +		==== ao_dma_set_isr + +			.... +			void +			ao_dma_set_isr(uint8_t index, void (*isr)(int)) +			.... + +			This sets a function to be called when the DMA +			transfer completes in lieu of setting the +			ao_dma_done bits. Use this when some work +			needs to be done when the DMA finishes that +			cannot wait until user space resumes. + +		==== ao_dma_start + +			.... +			void +			ao_dma_start(uint8_t id); +			.... + +			Arm the specified DMA engine and await a +			signal from either hardware or software to +			start transferring data.  'ao_dma_done[index]' +			is cleared when the DMA is started, and then +			receives the AO_DMA_DONE bit on a successful +			transfer or the AO_DMA_ABORTED bit if +			ao_dma_abort was called. Note that it is +			possible to get both bits if the transfer was +			aborted after it had finished. + +		==== ao_dma_done_transfer + +			.... +			void +			ao_dma_done_transfer(uint8_t id); +			.... + +			Signals that a specific DMA engine is done +			being used. This allows multiple drivers to +			use the same DMA engine safely. + +		==== ao_dma_abort + +			.... +			void +			ao_dma_abort(uint8_t id) +			.... + +			Terminate any in-progress DMA transaction, +			marking its 'done' variable with the +			AO_DMA_ABORTED bit. + +== Stdio interface + +	AltOS offers a stdio interface over USB, serial and the RF +	packet link. This provides for control of the device locally or +	remotely. This is hooked up to the stdio functions by providing +	the standard putchar/getchar/flush functions. These +	automatically multiplex the available communication channels; +	output is always delivered to the channel which provided the +	most recent input. + +	=== putchar + +		.... +		void +		putchar(char c) +		.... + +		Delivers a single character to the current console +		device. + +	=== getchar + +		.... +		char +		getchar(void) +		.... + +		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. + +	=== flush + +		.... +		void +		flush(void) +		.... + +		Flushes the current console device output buffer. Any +		pending characters will be delivered to the target device. + +	=== ao_add_stdio + +		.... +		void +		ao_add_stdio(char (*pollchar)(void), +				   void (*putchar)(char), +				   void (*flush)(void)) +		.... + +		This adds another console device to the available +		list. + +		'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. + +		'putchar' queues a character for output, flushing if the output buffer is +		full. It may block in this case. + +		'flush' forces the output buffer to be flushed. It may +		block until the buffer is delivered, but it is not +		required to do so. + +== Command line interface + +	AltOS includes a simple command line parser which is hooked up +	to the stdio interfaces permitting remote control of the +	device over USB, serial or the RF link as desired. Each +	command uses a single character to invoke it, the remaining +	characters on the line are available as parameters to the +	command. + +	=== ao_cmd_register + +		.... +		void +		ao_cmd_register(__code struct ao_cmds *cmds) +		.... + +		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: + +		.... +		\struct ao_cmds { +		\	char		cmd; +		\	void		(*func)(void); +		\	const char	*help; +		\}; +		.... +		'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: + +		ao_cmd_success:: + +		The command was parsed successfully. There is no need +		to assign this value, it is the default. + +		ao_cmd_lex_error:: + +		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. + +		ao_syntax_error:: + +		The command line is invalid for some reason other than +		invalid tokens. + +	=== ao_cmd_lex + +		.... +		void +		ao_cmd_lex(void); +		.... + +		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. + +	=== ao_cmd_put16 + +		.... +		void +		ao_cmd_put16(uint16_t v); +		.... + +		Writes 'v' as four hexadecimal characters. + +	=== ao_cmd_put8 + +		.... +		void +		ao_cmd_put8(uint8_t v); +		.... + +		Writes 'v' as two hexadecimal characters. + +	=== ao_cmd_white + +		.... +		void +		ao_cmd_white(void) +		.... + +		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. + +	=== ao_cmd_hex + +		.... +		void +		ao_cmd_hex(void) +		.... + +		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; + +	=== ao_cmd_decimal + +		.... +		void +		ao_cmd_decimal(void) +		.... + +		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; + +	=== ao_match_word + +		.... +		uint8_t +		ao_match_word(__code char *word) +		.... + +		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. + +	=== ao_cmd_init + +		.... +		void +		ao_cmd_init(void +		.... + +		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. + +== USB target device + +	AltOS contains a full-speed USB target device driver. It can +	be programmed to offer any kind of USB target, but to simplify +	interactions with a variety of operating systems, AltOS +	provides only a single target device profile, that of a USB +	modem which has native drivers for Linux, Windows and Mac OS +	X. It would be easy to change the code to provide an alternate +	target device if necessary. + +	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. + +	=== ao_usb_flush + +		.... +		void +		ao_usb_flush(void); +		.... + +		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. + +	=== ao_usb_putchar + +		.... +		void +		ao_usb_putchar(char c); +		.... + +		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. + +	=== ao_usb_pollchar + +		.... +		char +		ao_usb_pollchar(void); +		.... + +		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. + +	=== ao_usb_getchar + +		.... +		char +		ao_usb_getchar(void); +		.... + +		This uses ao_pollchar to receive the next character, +		blocking while ao_pollchar returns AO_READ_AGAIN. + +	=== ao_usb_disable + +		.... +		void +		ao_usb_disable(void); +		.... + +		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. + +		Note that neither TeleDongle v0.2 nor TeleMetrum v1 +		are able to signal to the USB host that they have +		disconnected, so after disabling the USB device, it's +		likely that the cable will need to be disconnected and +		reconnected before it will work again. + +	=== ao_usb_enable + +		.... +		void +		ao_usb_enable(void); +		.... + +		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. + +	=== ao_usb_init + +		.... +		void +		ao_usb_init(void); +		.... + +		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. + +== Serial peripherals + +	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. + +	To prevent loss of data, AltOS provides receive and transmit +	fifos of 32 characters each. + +	=== ao_serial_getchar + +		.... +		char +		ao_serial_getchar(void); +		.... + +		Returns the next character from the receive fifo, blocking +		until a character is received if the fifo is empty. + +	=== ao_serial_putchar + +		.... +		void +		ao_serial_putchar(char c); +		.... + +		Adds a character to the transmit fifo, blocking if the +		fifo is full. Starts transmitting characters. + +	=== ao_serial_drain + +		.... +		void +		ao_serial_drain(void); +		.... + +		Blocks until the transmit fifo is empty. Used internally +		when changing serial speeds. + +	=== ao_serial_set_speed + +		.... +		void +		ao_serial_set_speed(uint8_t speed); +		.... + +		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. + +	=== ao_serial_init + +		.... +		void +		ao_serial_init(void) +		.... + +		Initializes the serial peripheral. Call this from 'main' +		before jumping to ao_start_scheduler. The default speed +		setting is AO_SERIAL_SPEED_4800. + +== CC1111/CC1120/CC1200 Radio peripheral + +	=== Radio Introduction + +		The CC1111, CC1120 and CC1200 radio transceiver sends +		and receives digital packets with forward error +		correction and detection. The AltOS driver is fairly +		specific to the needs of the TeleMetrum and TeleDongle +		devices, using it for other tasks may require +		customization of the driver itself. There are three +		basic modes of operation: + +		. 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. + +		. 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. + +		  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. + +		  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. + +		. 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. + +	=== ao_radio_set_telemetry + +		.... +		void +		ao_radio_set_telemetry(void); +		.... + +		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. + +	=== ao_radio_set_packet + +		.... +		void +		ao_radio_set_packet(void); +		.... + +		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. + +	=== ao_radio_set_rdf + +		.... +		void +		ao_radio_set_rdf(void); +		.... + +		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. + +	=== ao_radio_idle + +		.... +		void +		ao_radio_idle(void); +		.... + +		Sets the radio device to idle mode, waiting until it reaches +		that state. This will terminate any in-progress transmit or +		receive operation. + +	=== ao_radio_get + +		.... +		void +		ao_radio_get(void); +		.... + +		Acquires the radio mutex and then configures the radio +		frequency using the global radio calibration and channel +		values. + +	=== ao_radio_put + +		.... +		void +		ao_radio_put(void); +		.... + +		Releases the radio mutex. + +	=== ao_radio_abort + +		.... +		void +		ao_radio_abort(void); +		.... + +		Aborts any transmission or reception process by aborting the +		associated DMA object and calling ao_radio_idle to terminate +		the radio operation. + +	=== Radio Telemetry + +		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. + +		==== ao_radio_send + +		.... +		void +		ao_radio_send(__xdata struct ao_telemetry *telemetry); +		.... + +		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. + +		==== ao_radio_recv + +		.... +		void +		ao_radio_recv(__xdata struct ao_radio_recv *radio); +		.... + +		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()). + +	=== Radio Direction Finding + +		In radio direction finding mode, there's just one function to +		use + +		==== ao_radio_rdf + +		.... +		void +		ao_radio_rdf(int ms); +		.... + +		This sends an RDF packet lasting for the specified amount +		of time. The maximum length is 1020 ms. + +	=== Radio Packet Mode + +		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. + +		==== ao_packet_putchar + +			.... +			void +			ao_packet_putchar(char c); +			.... + +			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. + +		==== ao_packet_pollchar + +			.... +			char +			ao_packet_pollchar(void); +			.... + +			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. + +		==== ao_packet_slave_start + +			.... +			void +			ao_packet_slave_start(void); +			.... + +			This is available only on the slave side and starts a task +			to listen for packet data. + +		==== ao_packet_slave_stop + +			.... +			void +			ao_packet_slave_stop(void); +			.... + +			Disables the packet slave task, stopping the radio receiver. + +		==== ao_packet_slave_init + +			.... +			void +			ao_packet_slave_init(void); +			.... + +			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. + +		==== ao_packet_master_init + +			.... +			void +			ao_packet_master_init(void); +			.... + +			Adds the 'p' packet forward command to start packet mode. diff --git a/doc/altos.xsl b/doc/altos.xsl deleted file mode 100644 index 6092dfcb..00000000 --- a/doc/altos.xsl +++ /dev/null @@ -1,1612 +0,0 @@ -<?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>1.1</revnumber> -        <date>05 November 2012</date> -        <revremark>Portable version</revremark> -      </revision> -      <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 a variety of -      microcontrollers used in Altus Metrum devices. It has a simple -      porting layer for each CPU while providing a convenient -      operating enviroment for the developer. AltOS currently -      supports three different CPUs: -      <itemizedlist> -	<listitem> -	  <para> -	    STM32L series from ST Microelectronics. This ARM Cortex-M3 -	    based microcontroller offers low power consumption and a -	    wide variety of built-in peripherals. Altus Metrum uses -	    this in the TeleMega, MegaDongle and TeleLCO projects. -	  </para> -	</listitem> -	<listitem> -	  <para> -	    CC1111 from Texas Instruments. This device includes a -	    fabulous 10mW digital RF transceiver along with an -	    8051-compatible processor core and a range of -	    peripherals. This is used in the TeleMetrum, TeleMini, -	    TeleDongle and TeleFire projects which share the need for -	    a small microcontroller and an RF interface. -	  </para> -	</listitem> -	<listitem> -	  <para> -	    ATmega32U4 from Atmel. This 8-bit AVR microcontroller is -	    one of the many used to create Arduino boards. The 32U4 -	    includes a USB interface, making it easy to connect to -	    other computers. Altus Metrum used this in prototypes of -	    the TeleScience and TelePyro boards; those have been -	    switched to the STM32L which is more capable and cheaper. -	  </para> -	</listitem> -      </itemizedlist> -      Among the features of AltOS are: -      <itemizedlist> -	<listitem> -	  <para>Multi-tasking. While microcontrollers often don't -	  provide separate address spaces, it's often easier to write -	  code that operates in separate threads instead of tying -	  everything into one giant event loop. -	  </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>AltOS Porting Layer</title> -    <para> -      AltOS provides a CPU-independent interface to various common -      microcontroller subsystems, including GPIO pins, interrupts, -      SPI, I2C, USB and asynchronous serial interfaces. By making -      these CPU-independent, device drivers, generic OS and -      application code can all be written that work on any supported -      CPU. Many of the architecture abstraction interfaces are -      prefixed with ao_arch. -    </para> -    <section> -      <title>Low-level CPU operations</title> -      <para> -	These primitive operations provide the abstraction needed to -	run the multi-tasking framework while providing reliable -	interrupt delivery. -      </para> -      <section> -	<title>ao_arch_block_interrupts/ao_arch_release_interrupts</title> -	<programlisting> -	  static inline void -	  ao_arch_block_interrupts(void); -	   -	  static inline void -	  ao_arch_release_interrupts(void); -	</programlisting> -	<para> -	  These disable/enable interrupt delivery, they may not -	  discard any interrupts. Use these for sections of code that -	  must be atomic with respect to any code run from an -	  interrupt handler. -	</para> -      </section> -      <section> -	<title>ao_arch_save_regs, ao_arch_save_stack, -	ao_arch_restore_stack</title> -	<programlisting> -	  static inline void -	  ao_arch_save_regs(void); - -	  static inline void -	  ao_arch_save_stack(void); - -	  static inline void -	  ao_arch_restore_stack(void); -	</programlisting> -	<para> -	  These provide all of the support needed to switch between -	  tasks.. ao_arch_save_regs must save all CPU registers to the -	  current stack, including the interrupt enable -	  state. ao_arch_save_stack records the current stack location -	  in the current ao_task structure. ao_arch_restore_stack -	  switches back to the saved stack, restores all registers and -	  branches to the saved return address. -	</para> -      </section> -      <section> -	<title>ao_arch_wait_interupt</title> -	<programlisting> -	  #define ao_arch_wait_interrupt() -	</programlisting> -	<para> -	  This stops the CPU, leaving clocks and interrupts -	  enabled. When an interrupt is received, this must wake up -	  and handle the interrupt. ao_arch_wait_interrupt is entered -	  with interrupts disabled to ensure that there is no gap -	  between determining that no task wants to run and idling the -	  CPU. It must sleep the CPU, process interrupts and then -	  disable interrupts again. If the CPU doesn't have any -	  reduced power mode, this must at the least allow pending -	  interrupts to be processed. -	</para> -      </section> -    </section> -    <section> -      <title>GPIO operations</title> -      <para> -	These functions provide an abstract interface to configure and -	manipulate GPIO pins. -      </para> -      <section> -	<title>GPIO setup</title> -	<para> -	  These macros may be invoked at system initialization time to -	  configure pins as needed for system operation. One tricky -	  aspect is that some chips provide direct access to specific -	  GPIO pins while others only provide access to a whole -	  register full of pins. To support this, the GPIO macros -	  provide both port+bit and pin arguments. Simply define the -	  arguments needed for the target platform and leave the -	  others undefined. -	</para> -	<section> -	  <title>ao_enable_output</title> -	  <programlisting> -	    #define ao_enable_output(port, bit, pin, value) -	  </programlisting> -	  <para> -	    Set the specified port+bit (also called 'pin') for output, -	    initializing to the specified value. The macro must avoid -	    driving the pin with the opposite value if at all -	    possible. -	  </para> -	</section> -	<section> -	  <title>ao_enable_input</title> -	  <programlisting> -	    #define ao_enable_input(port, bit, mode) -	  </programlisting> -	  <para> -	    Sets the specified port/bit to be an input pin. 'mode' is -	    a combination of one or more of the following. Note that -	    some platforms may not support the desired mode. In that -	    case, the value will not be defined so that the program -	    will fail to compile. -	    <itemizedlist> -	      <listitem> -<para> -		AO_EXTI_MODE_PULL_UP. Apply a pull-up to the pin; a -		disconnected pin will read as 1. -</para> -	      </listitem> -	      <listitem> -<para> -		AO_EXTI_MODE_PULL_DOWN. Apply a pull-down to the pin; -		a disconnected pin will read as 0. -</para> -	      </listitem> -	      <listitem> -<para> -		0. Don't apply either a pull-up or pull-down. A -		disconnected pin will read an undetermined value. -</para> -	      </listitem> -	    </itemizedlist> -	  </para> -	</section> -      </section> -      <section> -	<title>Reading and writing GPIO pins</title> -	<para> -	  These macros read and write individual GPIO pins. -	</para> -	<section> -	  <title>ao_gpio_set</title> -	  <programlisting> -	    #define ao_gpio_set(port, bit, pin, value) -	  </programlisting> -	  <para> -	    Sets the specified port/bit or pin to the indicated value -	  </para> -	</section> -	<section> -	  <title>ao_gpio_get</title> -	  <programlisting> -	    #define ao_gpio_get(port, bit, pin) -	  </programlisting> -	  <para> -	    Returns either 1 or 0 depending on whether the input to -	    the pin is high or low. -	  </para> -	</section> -      </section> -    </section> -  </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> -    <para> -      When built on other architectures, the various SDCC-specific -      symbols are #defined as empty strings so they don't affect the compiler. -    </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> -      <section> -	<title>__data</title> -	<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> -      </section> -      <section> -	<title>__idata</title> -	<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> -      </section> -      <section> -	<title>__xdata</title> -	<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> -      </section> -      <section> -	<title>__pdata</title> -	<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> -      </section> -      <section> -	<title>__code</title> -	<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> -      </section> -      <section> -	<title>__bit</title> -	<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> -      </section> -      <section> -	<title>__sfr, __sfr16, __sfr32, __sbit</title> -	<para> -	  Access to physical registers in the device use this mode -	  which declares the variable name, its type and the -	  address it lives at. No memory is allocated for these -	  variables. -	</para> -      </section> -    </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. AltOS doesn't use this form in shared -	  code as other compilers wouldn't know what to do. Use -	  ao_arch_block_interrupts and ao_arch_release_interrupts instead. -	</para> -      </section> -    </section> -  </chapter> -  <chapter> -    <title>Task functions</title> -    <para> -      This chapter documents how to create, destroy and schedule AltOS tasks. -    </para> -    <section> -      <title>ao_add_task</title> -      <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> -    </section> -    <section> -      <title>ao_exit</title> -      <programlisting> -	void -	ao_exit(void) -      </programlisting> -      <para> -	This terminates the current task. -      </para> -    </section> -    <section> -      <title>ao_sleep</title> -      <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> -      <para> -	Because ao_wakeup wakes every task waiting on a particular -	location, ao_sleep should be used in a loop that first checks -	the desired condition, blocks in ao_sleep and then rechecks -	until the condition is satisfied. If the location may be -	signaled from an interrupt handler, the code will need to -	block interrupts around the block of code. Here's a complete -	example: -	<programlisting> -	  ao_arch_block_interrupts(); -	  while (!ao_radio_done) -	          ao_sleep(&ao_radio_done); -	  ao_arch_release_interrupts(); -	</programlisting> -      </para> -    </section> -    <section> -      <title>ao_wakeup</title> -      <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. Here's an example of using this: -	<programlisting> -	  if (RFIF & RFIF_IM_DONE) { -	          ao_radio_done = 1; -	          ao_wakeup(&ao_radio_done); -	          RFIF &= ~RFIF_IM_DONE; -	  } -	</programlisting> -	Note that this need not block interrupts as the ao_sleep block -	can only be run from normal mode, and so this sequence can -	never be interrupted with execution of the other sequence. -      </para> -    </section> -    <section> -      <title>ao_alarm</title> -      <programlisting> -	void -	ao_alarm(uint16_t delay); - -	void -	ao_clear_alarm(void); -      </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. ao_clear_alarm resets any pending -	alarm so that it doesn't fire at some arbitrary point in the -	future. -	<programlisting> -	  ao_alarm(ao_packet_master_delay); -	  ao_arch_block_interrupts(); -	  while (!ao_radio_dma_done) -	          if (ao_sleep(&ao_radio_dma_done) != 0) -	                  ao_radio_abort(); -	  ao_arch_release_interrupts(); -	  ao_clear_alarm(); -	</programlisting> -	In this example, a timeout is set before waiting for -	incoming radio data. If no data is received before the -	timeout fires, ao_sleep will return 1 and then this code -	will abort the radio receive operation. -      </para> -    </section> -    <section> -      <title>ao_start_scheduler</title> -      <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> -    </section> -    <section> -      <title>ao_clock_init</title> -      <programlisting> -	void -	ao_clock_init(void); -      </programlisting> -      <para> -	This initializes the main CPU clock and switches to it. -      </para> -    </section> -  </chapter> -  <chapter> -    <title>Timer Functions</title> -    <para> -      AltOS sets up one of the CPU timers to run at 100Hz and -      exposes this tick as the fundemental unit of time. At each -      interrupt, AltOS increments the counter, and schedules any tasks -      waiting for that time to pass, then fires off the sensors to -      collect current data readings. Doing this from the ISR ensures -      that the values are sampled at a regular rate, independent -      of any scheduling jitter. -    </para> -    <section> -      <title>ao_time</title> -      <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> -    </section> -    <section> -      <title>ao_delay</title> -      <programlisting> -	void -	ao_delay(uint16_t ticks); -      </programlisting> -      <para> -	Suspend the current task for at least 'ticks' clock units. -      </para> -    </section> -    <section> -      <title>ao_timer_set_adc_interval</title> -      <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> -    </section> -    <section> -      <title>ao_timer_init</title> -      <programlisting> -	void -	ao_timer_init(void) -      </programlisting> -      <para> -	This turns on the 100Hz tick. It is required for any of the -	time-based functions to work. It should be called by 'main' -	before ao_start_scheduler. -      </para> -    </section> -  </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> -    <section> -      <title>ao_mutex_get</title> -      <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> -    </section> -    <section> -      <title>ao_mutex_put</title> -      <programlisting> -	void -	ao_mutex_put(__xdata uint8_t *mutex); -      </programlisting> -      <para> -	Releases the specified mutex, waking up all tasks waiting -	for it. -      </para> -    </section> -  </chapter> -  <chapter> -    <title>DMA engine</title> -    <para> -      The CC1111 and STM32L both contain a useful bit of extra -      hardware in the form of a number of programmable DMA -      engines. They can be configured to copy data in memory, or -      between memory and devices (or even between two devices). AltOS -      exposes a general interface to this hardware and uses it to -      handle both internal and external devices. -    </para> -    <para> -      Because the CC1111 and STM32L DMA engines are different, the -      interface to them is also different. As the DMA engines are -      currently used to implement platform-specific drivers, this -      isn't yet a problem. -    </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> -    <section> -      <title>CC1111 DMA Engine</title> -      <section> -	<title>ao_dma_alloc</title> -	<programlisting> -	  uint8_t -	  ao_dma_alloc(__xdata uint8_t *done) -	</programlisting> -	<para> -	  Allocate a DMA engine, returning the identifier.  'done' is -	  cleared when the DMA is started, and then receives the -	  AO_DMA_DONE bit on a successful transfer or the -	  AO_DMA_ABORTED bit if ao_dma_abort was called. Note that it -	  is possible to get both bits if the transfer was aborted -	  after it had finished. -	</para> -      </section> -      <section> -	<title>ao_dma_set_transfer</title> -	<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> -      </section> -      <section> -	<title>ao_dma_start</title> -	<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> -      </section> -      <section> -	<title>ao_dma_trigger</title> -	<programlisting> -	  void -	  ao_dma_trigger(uint8_t id) -	</programlisting> -	<para> -	  Trigger the specified DMA engine to start copying data. -	</para> -      </section> -      <section> -	<title>ao_dma_abort</title> -	<programlisting> -	  void -	  ao_dma_abort(uint8_t id) -	</programlisting> -	<para> -	  Terminate any in-progress DMA transaction, marking its -	  'done' variable with the AO_DMA_ABORTED bit. -	</para> -      </section> -    </section> -    <section> -      <title>STM32L DMA Engine</title> -      <section> -	<title>ao_dma_alloc</title> -	<programlisting> -	  uint8_t ao_dma_done[]; - -	  void -	  ao_dma_alloc(uint8_t index); -	</programlisting> -	<para> -	  Reserve a DMA engine for exclusive use by one -	  driver. -	</para> -      </section> -      <section> -	<title>ao_dma_set_transfer</title> -	<programlisting> -	  void -	  ao_dma_set_transfer(uint8_t id, -	  void *peripheral, -	  void *memory, -	  uint16_t count, -	  uint32_t ccr); -	</programlisting> -	<para> -	  Initializes the specified dma engine to copy data between -	  'peripheral' and 'memory' for 'count' units. 'ccr' is a -	  value directly out of the STM32L documentation and tells the -	  DMA engine what the transfer unit size, direction and step -	  are. -	</para> -      </section> -      <section> -	<title>ao_dma_set_isr</title> -	<programlisting> -	  void -	  ao_dma_set_isr(uint8_t index, void (*isr)(int)) -	</programlisting> -	<para> -	  This sets a function to be called when the DMA transfer -	  completes in lieu of setting the ao_dma_done bits. Use this -	  when some work needs to be done when the DMA finishes that -	  cannot wait until user space resumes. -	</para> -      </section> -      <section> -	<title>ao_dma_start</title> -	<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. -	  'ao_dma_done[index]' is cleared when the DMA is started, and -	  then receives the AO_DMA_DONE bit on a successful transfer -	  or the AO_DMA_ABORTED bit if ao_dma_abort was called. Note -	  that it is possible to get both bits if the transfer was -	  aborted after it had finished. -	</para> -      </section> -      <section> -	<title>ao_dma_done_transfer</title> -	<programlisting> -	  void -	  ao_dma_done_transfer(uint8_t id); -	</programlisting> -	<para> -	  Signals that a specific DMA engine is done being used. This -	  allows multiple drivers to use the same DMA engine safely. -	</para> -      </section> -      <section> -	<title>ao_dma_abort</title> -	<programlisting> -	  void -	  ao_dma_abort(uint8_t id) -	</programlisting> -	<para> -	  Terminate any in-progress DMA transaction, marking its -	  'done' variable with the AO_DMA_ABORTED bit. -	</para> -      </section> -    </section> -  </chapter> -  <chapter> -    <title>Stdio interface</title> -    <para> -      AltOS offers a stdio interface over USB, serial and the RF -      packet link. This provides for control of the device locally or -      remotely. This is hooked up to the stdio functions by providing -      the standard putchar/getchar/flush functions. These -      automatically multiplex the available communication channels; -      output is always delivered to the channel which provided the -      most recent input. -    </para> -    <section> -      <title>putchar</title> -      <programlisting> -	void -	putchar(char c) -      </programlisting> -      <para> -	Delivers a single character to the current console -	device. -      </para> -    </section> -    <section> -      <title>getchar</title> -      <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> -    </section> -    <section> -      <title>flush</title> -      <programlisting> -	void -	flush(void) -      </programlisting> -      <para> -	Flushes the current console device output buffer. Any -	pending characters will be delivered to the target device. -      </para> -    </section> -    <section> -      <title>ao_add_stdio</title> -      <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> -    </section> -  </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, serial or the RF link as desired. Each command uses a -      single character to invoke it, the remaining characters on the -      line are available as parameters to the command. -    </para> -    <section> -      <title>ao_cmd_register</title> -      <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> -    </section> -    <section> -      <title>ao_cmd_lex</title> -      <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> -    </section> -    <section> -      <title>ao_cmd_put16</title> -      <programlisting> -	void -	ao_cmd_put16(uint16_t v); -      </programlisting> -      <para> -	Writes 'v' as four hexadecimal characters. -      </para> -    </section> -    <section> -      <title>ao_cmd_put8</title> -      <programlisting> -	void -	ao_cmd_put8(uint8_t v); -      </programlisting> -      <para> -	Writes 'v' as two hexadecimal characters. -      </para> -    </section> -    <section> -      <title>ao_cmd_white</title> -      <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> -    </section> -    <section> -      <title>ao_cmd_hex</title> -      <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> -    </section> -    <section> -      <title>ao_cmd_decimal</title> -      <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> -    </section> -    <section> -      <title>ao_match_word</title> -      <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> -    </section> -    <section> -      <title>ao_cmd_init</title> -      <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> -    </section> -  </chapter> -  <chapter> -    <title>USB target device</title> -    <para> -      AltOS contains a full-speed USB target device driver. It can be -      programmed to offer any kind of USB target, but to simplify -      interactions with a variety of operating systems, AltOS provides -      only a single target device profile, that of a USB modem which -      has native drivers for Linux, Windows and Mac OS X. It would be -      easy to change the code to provide an alternate target device if -      necessary. -    </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> -    <section> -      <title>ao_usb_flush</title> -      <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> -    </section> -    <section> -      <title>ao_usb_putchar</title> -      <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> -    </section> -    <section> -      <title>ao_usb_pollchar</title> -      <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> -    </section> -    <section> -      <title>ao_usb_getchar</title> -      <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> -    </section> -    <section> -      <title>ao_usb_disable</title> -      <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> -    </section> -    <section> -      <title>ao_usb_enable</title> -      <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> -    </section> -    <section> -      <title>ao_usb_init</title> -      <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> -    </section> -  </chapter> -  <chapter> -    <title>Serial peripherals</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> -    <section> -      <title>ao_serial_getchar</title> -      <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> -    </section> -    <section> -      <title>ao_serial_putchar</title> -      <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> -    </section> -    <section> -      <title>ao_serial_drain</title> -      <programlisting> -	void -	ao_serial_drain(void); -      </programlisting> -      <para> -	Blocks until the transmit fifo is empty. Used internally -	when changing serial speeds. -      </para> -    </section> -    <section> -      <title>ao_serial_set_speed</title> -      <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> -    </section> -    <section> -      <title>ao_serial_init</title> -      <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> -    </section> -  </chapter> -  <chapter> -    <title>CC1111 Radio peripheral</title> -    <section> -      <title>Radio Introduction</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> -    </section> -    <section> -      <title>ao_radio_set_telemetry</title> -	<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> -    </section> -    <section> -      <title>ao_radio_set_packet</title> -	<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> -    </section> -    <section> -      <title>ao_radio_set_rdf</title> -	<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> -    </section> -    <section> -      <title>ao_radio_idle</title> -	<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> -    </section> -    <section> -      <title>ao_radio_get</title> -	<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> -    </section> -    <section> -      <title>ao_radio_put</title> -	<programlisting> -	  void -	  ao_radio_put(void); -	</programlisting> -	<para> -	  Releases the radio mutex. -	</para> -    </section> -    <section> -      <title>ao_radio_abort</title> -	<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> -    </section> -    <section> -      <title>Radio Telemetry</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> -      <section> -	<title>ao_radio_send</title> -	<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> -      </section> -      <section> -	<title>ao_radio_recv</title> -	<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> -      </section> -    </section> -    <section> -      <title>Radio Direction Finding</title> -      <para> -	In radio direction finding mode, there's just one function to -	use -      </para> -      <section> -	<title>ao_radio_rdf</title> -	<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> -      </section> -    </section> -    <section> -      <title>Radio Packet Mode</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> -      <section> -	<title>ao_packet_putchar</title> -	<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> -      </section> -      <section> -	<title>ao_packet_pollchar</title> -	<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> -      </section> -      <section> -	<title>ao_packet_slave_start</title> -	<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> -      </section> -      <section> -	<title>ao_packet_slave_stop</title> -	<programlisting> -	  void -	  ao_packet_slave_stop(void); -	</programlisting> -	<para> -	  Disables the packet slave task, stopping the radio receiver. -	</para> -      </section> -      <section> -	<title>ao_packet_slave_init</title> -	<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> -      </section> -      <section> -	<title>ao_packet_master_init</title> -	<programlisting> -	  void -	  ao_packet_master_init(void); -	</programlisting> -	<para> -	  Adds the 'p' packet forward command to start packet mode. -	</para> -      </section> -    </section> -  </chapter> -</book>  | 
