Go to the first, previous, next, last section, table of contents.
These functions read or write from the standard input and output
streams for a process. By default, the standard io streams will be
multiplexed serial streams.
Standard ANSI stdio functions
- `int getchar();'
-
- `int getc(FILE *stream);'
-
Both of these functions return the next character from the
input buffer that has not yet been read. If there is an
error, like when the stream argument is invalid, a -1 is
returned. getchar() returns the next character from the
default input stream for the process calling it. getc()
returns the next character from the given stream. getchar()
is frequently used in programs to wait until the user is
ready to continue (The old "press any key to continue"
prompt). Keep in mind that a buffering getchar will cause
execution to continue if there is any past input left in the
buffer, so you may want to call clear_serial_output_buffer()
immediately before the call to getchar for this purpose.
- `int putchar(char ch);'
-
- `int putc(char ch, FILE *stream);'
-
Both of these functions write the given character to the
output queue to be sent over the serial line. Because serial
output is buffered, these functions will return after writing
to the output buffer. If the buffer is full, these functions
will not return until there is sufficient space in the buffer
to write the next character. Because all other serial
output functions (puts(), printf, and its derivatives) call
putchar, this applies to all of them.
- `char *gets(char *s);'
-
Gets a string from over the serial line, echoes it, and places
it into the character array s. s must have enough space
allocated to handle the input or overflow will occur. gets()
takes care of delete and backspace characters appropriately and
returns the pointer to the null terminated string upon
receiving a return character. I recommend that you do not use
gets(s), but rather use fgets(stdin,length,s) with length
set to be the space allocated to s. This is a much safer in
situations where overflow is possible.
- `int sscanf(char *s, char *format, ...);'
-
Standard sscanf which scans the input string s
and writes the appropriate values to it's arguments.
A modification of this beyond standard is that
a %b format string parses the binary representation
of a number.
- `int scanf(char *format, ...);'
-
Standard scanf which calls sscanf with the results
of a gets. The input length is limited to 100 characters.
- `int puts(char *ptr);'
-
Writes a null terminated character string to standard output
by calling putchar on each character.
- `int printf(char *format, ...);'
-
Standard printf (look in a C reference for
specs) using putchar to output each character to
standard output the modification that %#b
prints the binary representation of a number to
# (in the range 1 to 32) binary digits. If # is
left out, the defaults is to display 8 bits.
- `int sprintf(char *s, char *format, ...);'
-
Standard sprintf which writes to the character string s
instead of an output stream.
Kernel extensions of stdio facilities
- `void clear_serial_input_buffer();'
-
- `void clear_serial_output_buffer();'
-
These functions clear the I/O buffers. This will result
in data lossage, but sometimes you want that.
- `void wait_for_serial_output_complete();'
-
This function returns when the standard output buffer is empty.
- `int serial_data_received();'
-
Returns a 1 if there is data in the stadard input buffer, otherwise
returns 0. This is a useful loop condition if you want a
keyboard hit to cause a loop to exit.
- `int serial_input_buffer_depth();'
-
- `int serial_output_buffer_depth();'
-
These functions return the depth of the standard I/O buffers.
The input buffer depth is the number of characters received
but not yet recovered through a call to getchar(). The
output buffer depth is the number of characters written
to the output buffer but not yet removed from the buffer.
If the standard output is a multiplexed stream, this depth
may reflect the activity of other processes as well.
These functions open, close, read from or write to files and streams.
The file abstraction module allows io to be treated in similar way to
how it would be treated on other c systems. There are still no real
files (the memory is not permanent or large enough to really justify
it), but the file abstraction provides a consistent interface to io
drivers.
The "file" name structure simulates a 2-deep directory hierarchy
where the "directory" name is the driver and the "filename" is the
name of the stream. Once created with fopen, files exist until all
instances of it are destroyed or until reset. If a file with the
same name is opened in multiple progrograms, processes, etc. all will
get a pointer to the same file object.
This allows for inter-process communication streams, or multiple
processes watching the same hardware io stream, etc. However, once
anyone removes a character from the file it's gone. There are not
independent file pointers.
Files have an input and an output queue (which may point to the same
place) which are fixed-length circular buffers. If a write is
attempted to a queue which is full, the write will either hang or
return -1 depending on the file type.
There are currently four drivers: stream, serial, seriala, and serialb.
- Stream is a wrapper around a single queue. Writes to the stream
insert characters into the queue, reads read characters from
the queue. Usage: `fopen("/stream/foo","");'
- Serial files do the obvious thing: writes send data out the
serial port and reads read from the serial port. It is
possible to specify "R" in the mode string to fopen (arg 2)
in order to get a raw stream. A raw stream gets every character
that is received at the serial port and sends characters directly
to the serial port. If "R" is not specified, the multi-threaded
stream protocol (used by arc and multiterm) is applied to the
stream. Usage: `fopen("/serial/foo","")' or
`fopen("/serial/foo","R")'.
- `/serial/foo' gets the default port specified by the
default_port
persistent (0 for a, 1 for b).
- `/seriala/foo' always gets porta.
- `/serialb/foo' always gets portb (if it exists).
The standard files stdin, and stdout refer to the serial stream on the
default port which is created automatically for the current process.
The standard file stderr refers to the console stream.
The following standard functions are available and should do what you expect:
- FILE *fopen(const char *filename,const char *mode);
- int fclose(FILE *s);
- int fgetc(FILE *f);
- int fputc(int c,FILE *f);
- int fpeekc(FILE *f);
- int fgets(FILE *f,int n,char *s);
- size fread(char *buff,size_t element_size,size_t count,FILE *f);
- size fwrite(char *buff,size_t element_size,size_t count,FILE *f);
- int fputs(FILE *f,char *s);
- int fprintf (FILE *f, char *format, ...);
- int vfprintf (FILE *f, char *format, va_list arg);
- int vsprintf (char *s, char *format, va_list arg);
- int vsputc (char c, FILE *f);
- int vsscanf (char *s, char *format, va_list arg);
- char *gets(char *s);
The following I defined because I thought they would be useful:
- `int fflush(FILE *s);'
-
Waits until the output buffer is empty.
- `int fclear(FILE *f)'
-
Clears the input queue to file f.
- `int fdepth(FILE *f)'
-
Returns the number of bytes in the input queue of the file.
The following functions are defined as specified in "C A Reference"
to conform to ANSI standard:
- String functions
- char *strcat(char *dest,const char *src);
- char *strncat(char *dest,const char *src,size_t n);
- int strcmp(const char *s1,const char *s2);
- int stricmp(const char *s1,const char *s2);
- int strncmp(const char *s1,const char *s2,size_t n);
- char *strcpy(char *dest,const char *src);
- char *strncpy(char *dest,const char *src,size_t n);
- size_t strlen(const char *s);
- const char *strchr(const char *s,int c);
- const char *strrchr(const char *s,int c);
- Memory functions
- void *memchr(void *ptr,int val,size_t len);
- int memcmp(void *ptr1,void *ptr2,size_t len);
- char *memcpy(void *dest,void *src,size_t len);
- void *memset(void *ptr,int val,size_t len);
- void memswap(void *a, void *b, size_t len);
- Sorting functions
void bssort(void *base, size_t n_elems, size_t elem_size,
int (*compare)(void *, void *));
This has the same arguments as standard qsort, but does a bubble
sort instead. There is no standard qsort at this time.
- `void *malloc(size_t size);'
-
- `void free(void *mem);'
-
These functions are as documented in C texts. malloc() takes a
integral size as an argument and returns a pointer to a block of
memory of that size, or NULL if there is not enough memory left.
free() takes a pointer returned from a previous call to malloc()
and frees that block of memory for reuse. Call free with a
non-malloced pointer or a previously freed pointer at your own
risk. If free detects that the pointer is not valid it will
breakpoint. You may then want to use gdb to find out where it
crashed.
Note that all of the memory malloced by a process is
automatically freed when that process dies. Therefore be
careful not to use memory malloced by one process in another
process unless you are confident that the proces which did the
malloc has a longer lifetime. It is in general safe for child
processes to use memory which the parent had mallocced because
they will die with the parent.
- `void *durable_malloc(size_t size);'
-
This function is just like malloc except that memory allocated
using durable_malloc is not automatically freed when the process
which allocated it dies and may exist even across downloads.
It is possible for durable_malloc to fail when regular malloc
would succeed, as it has a smaller heap to allocate from, so
you should use malloc when you don't need this extra feature.
Persistents provide a way to store state between resets and downloads,
implement inter-program communication, and adjust program parameters
from the console.
A persistent is really a permanently allocated 4-byte memory location
associated with a unique string. Persistents are created by calls to
`init_persistent()' and stored in a hash table. Any program may
call `init_persistent' or `lookup_persistent' and receive a
pointer to the same permanently allocated memory.
Persistents age at every reset, and persistents which have not been
accessed in long enough (default of 10 resets) are garbage collected.
The type `persistent' is a pointer to a volatile integer. The
following is a sample usage of persistents:
persistent foo;
void main()
{
foo=init_persistent("foo",4);
if(foo)
printf("Value stored in foo is %d\n",*foo);
else
printf("Persistent table full\n");
}
The size of the persistent table is reconfigurable, but requires a
recompile of kernel. The default is 256 entries with 16-character
hashing strings, which comes to 8K of memory usage.
- `persistent init_persistent(char *s,int defval);'
-
Look up the string s in the persistent table. If it is already
there, return the associated memory pointer. If it is not
already there, create it with the provided default value. If
there are no slots left in the persistent table, returns NULL.
If the string is too long, only the first N characters are used,
where N is a length built into the kernel -- defaulting to 16.
- `persistent set_persistent(char *s,int val);'
-
The same as init_persistent, only the value is set to val
regardless of whether it was already there or not.
- `persistent lookup_persistent(char *s);'
-
Looks up the string s in the persistent table and returns either
the associated memory pointer or NULL if it was not found.
- `int delete_persistent(char *s);'
-
Deletes the persistent from the persistent table on the next
reset by setting its age to be ancient. It does not delete the
entry due to the danger that someone else might have the handle
to the persistent.
- `void print_persistents();'
-
Prints the entire persistent table. This function is called by
the "persist" console command with no arguments.
A queue is a first-in-first-out (FIFO) data structure which is specially
designed for use in the multitasking environment with shared memory
which kernel provides. So long as only the accessor functions
`read_byte_from_queue()' and `write_byte_to_queue()' are used,
queue reads and writes are atomic. Therefore queues can be used by
multiple processes without extra locking. Queues use interrupt masking
to achive atomicity, so it is safe to use them in interrupt handlers
(unlike `lock()')
- `void initialize_queue(queue *qp,int size,unsigned char *data);'
-
Given a queue pointer, a size, and a pointer to a block of data
of the specified number of bytes, initialize_queue() initializes
the queue to be empty. Because queues must conform to certain
representation invariants in order to work properly, they should be
initialized before they can be used.
- `int init_queue(queue *qp,int size);'
-
- `int free_queue(queue *qp);'
-
init_queue() is just like initialize_queue() except that it
mallocs the data buffer of the specified size. If there is not
enough memory left to allocate the data, returns -1, otherwise
returns 0. There are times you want to use this for convenience.
A queue that has been initialized using init_queue must be freed
using free_queue() in order to free its storage (or you could
just free qp->data, but this has better abstraction qualities).
- `queue *create_queue(int size);'
-
- `int destroy_queue(queue *qp);'
-
These two functions are the same as the ones above, but they will
allocate and free the queue as well. It is not really necessary to
provide all of these functions, but it's basically free to do so
and there are occasions that require each of these methods.
- `int write_byte_to_queue(queue *qp,unsigned char b);'
-
Write a byte to the queue. If the queue is full, then the status
code
QUEUE_FULL
will be returned. Otherwise, b will be
atomically added to the end of the queue.
- `int read_byte_from_queue(queue *qp);'
-
Read a byte from the queue. If the queue is empty, then the status
code
QUEUE_EMPTY
will be returned. Otherwise, it will return the
first byte in the queue. (The type is int instead of char so
that error conditions are unambiguous.)
- `int peek_queue(queue *qp);'
-
Returns
QUEUE_EMPTY
if the queue is empty, otherwise returns the next
byte in the queue without removing it. You are not guaranteed that the
same byte will be there on a subsequent read if another process is trying
to read the queue.
- `int queue_depth(queue *qp);'
-
Returns the number of bytes currently in the queue.
- `void clear_queue(queue *qp);'
-
Throw away the current contents of a queue and make it empty.
A concern in multitasking systems with shared memory is achieving
mutual exclusion of certain operations. For example, if you had a
insertion/deletion table, you would want only one process to be able
to add an entry at a time to prevent the possibility of two processes
trying to add different entries to the same slot at the same time.
The functions in lock.c and lock.h provide one way of achieving this
mutual exclusion.
- `int test_and_set(lock_t *flag);'
-
Given a pointer to a lock flag, this function will atomically
test and set the value pointed at (by using the 68000 tas
instruction). It will return 0 if someone else already had the
lock, and 1 if no one else already had the lock.
- `void lock(lock_t *flag);'
-
- `void unlock(lock_t *flag);'
-
Given a pointer to a lock flag,
lock()
will defer in a loop so
long as someone else is holding the lock, and return when it has
achieved the lock itself.
Unlock clears a lock, and should ALWAYS be used after lock()
in
all possible execution paths so that the next thread (or the
same thread on another pass) can continue.
*** IMPORTANT ***
Lock should not be used by interrupt routines, as it runs the
risk of leaving the processor running at too high an inerrupt
level and blocking other interrupts which can mess things up.
There is a bit of a safety net built in which prevents lock from
blocking if the interrupt level is non-zero. However, this means
that interrupt routines can break the invariants that lock was
designed to protect -- so just don't do it.
These functions affect the interrupt vector table directly.
- `void install_exception_handler(int i,void *p);'
-
Installs the pointer p to an interrupt handler to correspond
to interrupt i (in the range 0 to 256). After calling this
function once to set up the vector, when the processor is
interrupted with exception number i, it will call the handler
pointed at by p.
The handler pointed to by p must be in assembler in order to preserve
registers properly and execute the needed
rte
(return from
exception) instruction instead of the default rts
(return from
subroutine) instruction.
Here is an example of a suitable interrupt handler -- you should change
the names of the functions, keeping in mind that C variable and function
names have a prepended underscore when you refer to them in assembler,
and vice versa (so "foo" in C becomes `_foo' in assembler, and
`_bar' in assembler is `bar' in C).
asm(".globl _my_handler
_my_handler:
moveml #0xc0c0,sp@- /* push registers d0, d1, a0, a1 */
jsr _my_c_handler /* call your c handler */
moveml sp@+,#0x0303 /* pop registers d0, d1, a0, a1 */
rte
");
void my_c_handler()
{
/* Do what you want to do on the interrupt here */
}
void main()
{
install_exception_handler(INTERRUPT_NUM,my_handler);
/* Now your exception handler is installed */
}
*** IMPORTANT ***
When user code exits, it is necessary to remove all exception
vectors that point into that code and replace them with
something safer. Therefore install_exception_handler() also
keeps track of which exception vectors a process installs and
removes them when the process exits. Keep this in mind and
make sure that the process which calls
install_exception_handler() is at least as long lived as any
process that depends on that exception vector being set.
- `void *get_exception_vector(int i);'
-
Returns the pointer to the interrupt handler for interrupt i.
- `uint *get_vbr();'
-
You probably won't need to use this function because
install_exception_handler() exists to protect you from it, but it will
return to you the vector base register currently being used by the
processor. The VBR is used during exception handling to know where the
vector table starts so the correct interrupt handler can be used
automatically. Be aware that with the way the system software is
currently arranged, if you change the VBR you will likely be hurting
yourself. Also, there is a default place that the system software puts
the vector table and install_exception_handler() assumes that it doesn't
move.
The multitasker takes care of process table management, process
scheduling, crash handing, and time keeping (with time kept in swapper
ticks). The scheduling interrupt on the 332 runs on the periodic
interrupt so that it makes no assumptions about the hardware function
allocation (in other words, it would be in many ways more convenient
to use a TPU interrupt, but it would conflict with someone using all 16
timer lines for servos). The period of the scheduling interrupt is
programmable. It is also possible for a process to give up the CPU
before its allocated time is up by calling defer() or one of its
derivatives. This uses a trap to cause an immediate swap to occur.
Process scheduling is round robin. When the current process is preempted
or defers the next process in the table is allowed to run.
I have been warned that I use the word "swap" incorrectly because swap
implies that things are being put on disk. None of the 332 systems this
software has yet been run on even have disks, so hopefully this
potential confusion is not a problem. "Swap" is simply a more
convenient term than "context switch" or anything else I can think of,
so I will use it.
I have also been warned that there is a distinction between processes
and threads. To many people, "processes" implies heavy-weight,
virtual memory changing, belonging to different programs, UNIX style
processes, whereas "threads" implies threads of execution which have
shared memory space and come from the same program. Kernel processes
are not exactly like either of these -- all processes have shared memory
space, and an arbitrary number of processes can be associated with
a program.
There is a limit to the number of processes which can be active at a
given time. Currently that limit is 32. I could change this if it ever
becomes necessary.
The functions described in this section make up the user's interface to
the multitasker.
- `typedef void (*ps_func)();'
-
- `pid start_process(const char *name,ps_func f,int stack_size,int time_slice);'
-
- `pid start_adult_process(const char *name,ps_func f,int stack_size,int time_slice);'
-
Both of these functions create and return the pid of a new
process with the specified name, stack_size bytes of stack,
time_slice scheduler ticks between being swapped in and being
preempted (if it doesn't defer beforehand), and which is started
with a call to function f (a void (void) function). The
difference between start_process() and start_adult_process() is
how they react to their parent being killed.
start_adult_process() starts a process with a completely
independent lifespan -- it will live until function f exits or
until it is killed. start_process() starts a standard child
process which will die on the same conditions or when its parent
dies, whichever comes first.
- `int kill_process(pid id);'
-
kill_process() kills the process with the given pid. If the
process is valid and killable (not root or gdb), kill_process()
kills it and returns 1. Otherwise it does nothing and returns 0.
- `pid get_current_pid();'
-
Returns the pid of the process currently executing.
- `pid lookup_pid_by_name(char *name);'
-
Given a name, lookup_pid_by_name will either return the pid of
the first process in the table with that name, or INVALID_PROCESS
if it does not exist. This is the function called by the kill
console command when it is given a string argument.
- `int set_swap_tick_period(int ns);'
-
- `int get_swap_tick_period();'
-
set_swap_tick_period() sets the period of the scheduler interrupt
to the number of nanoseconds specified and returns the period as
actually set (which will probably differ due to the granular
nature of the periodic interrupt setup). get_swap_tick_period()
returns the current scheduler tick period.
- `int swap_cycle_time();'
-
Returns the average number of scheduler ticks that it is taking
to completely cycle through the process table. This number will
range from 0 (if everyone is deferring) to the sum of the number
of ticks alotted to all the processes (if no one is deferring).
This number allows you to have an idea of the processor load.
- `void enable_process_swapping();'
-
- `void disable_process_swapping();'
-
These two functions allow the scheduler interrupt to be enabled
and disabled. Disabling the interrupt allows CPU-hungry
applications, such as software frame grabbing, to proceed
uninterrupted. However, be aware that while process swapping is
disabled the time will not increment so by using these functions
you are accepting that you will have inconsistent time.
- `void defer();'
-
defer() allows a process to give up the rest of its time and
allow the next process in the cycle to be swapped in right away.
defer() uses a trap to achieve this, as successful context
switching requires the use of an rte instruction to get the
status register right. Be aware that defer() has no effect on
when the next regularly spaced scheduler tick will fall.
Therefore, after a defer the next tick will be short. Therefore, if you
care about a process getting a certain amount of work done before
it is preempted you should let it have at least 2 ticks because
the first one may not be complete.
- `void defer_on_input_pending(FILE *f);'
-
`defer_on_input_pending()' marks the current process as an
io_waiting process and it will not be swapped in until there is
something in the input buffer of file f. This is automatically
called by getchar() on an empty input buffer.
- `void defer_until(int endtime);'
-
`defer_until()' marks the current process as a sleeping process and
it will not be swapped in until the time is greater than or equal
to endtime. This is automatically called by sleep() when the
duration of the sleep exceeds the number of ticks left to the
process.
- `void hog_processor();'
-
`hog_processor()' gives the current process 256 more scheduler
ticks. This is useful if you want to be fairly confident that
you can carry out a computation uninterrupted but do not want to
prevent the steady march of time.
- `int set_process_time_slice(pid id,int new_slice);'
-
Allows the time slice of a running process to be changed.
Returns 1 if the pid was valid and -1 if the pid was invalid.
- `int get_process_time_slice(pid id);'
-
Returns the current time slice for a process, or -1 if the pid is
invalid.
- `FILE *get_process_instream(pid id);'
-
Returns the input stream of a process so that another process
could talk to it over its standard text input stream if it wanted
to.
- `int time();'
-
Returns the current time;
- `int sleep(int counts);'
-
Sleeps for an amount of time greater than or equal to counts
scheduler ticks. The sleeping time may not be anywhere near
accurate if someone is turning off scheduler interrupts, and may
be larger by an amount less than or equal to swap_cycle_time().
- `void print_process_regs(pid id,FILE *s);'
-
Print the registers of the given process to the given stream.
- `void print_process_table();'
-
Print the process table. This is what is called by the ps
console command. An example of the output from this function
follows:
Multitasking:
swap cycle time = 1, expected = 16, load average = 6%
PID Status Ticks Low Water High PC Name
0 Running 5 13f800 13fd10 140000 702 Root process
33 IO Wait 10 130ca4 131334 1314a4 50d0 Debug Process
66 Crashed 1 1315e8 1318b0 1319e8 118144 Dummy process
The hardware registers for the 332 are memory mapped as bitfield
structures with the same names as in the Motorola documentation. See
the file mem.h in the kernel subdirectory of the main 332 tree for
details. Basically you have the following pointers to the main blocks
of 332 hardware registers, which work in both your programs and in
gdb:
- `simptr sim;'
-
Pointer to the system integration module memory map.
Some examples are:
sim->syncr - the clock control register
sim->porte - the data byte for port E
sim->picr.piv - the periodic control register interrupt vector number
Generally, if you are tempted to use these registers in your
programs you might want to check sys.h and sys.c to see if there
is a function that already does what you want.
- `csmptr csm;'
-
Pointer to the chip select module memory map.
Some examples are:
csm->boot - the complete chip select settings for CSBOOT
csm->csch[3].dsack - the data strobe acknowdge delay for CS3
Functions for setting up chip selects and changing their dsack
delay can be found in sys.h and sys.c.
- `qsmptr qsm;'
-
Pointer to the queued serial module memory map. This includes
the control and data registers for the queued serial module and
the asynchronous serial module both.
Some examples are:
qsm->tmcr.qmcr.iarb - the interrupt arbitration value for qsm
qsm->scbr - the asynchronous serial baud rate conrol register
Functions for controlling the asynchronous serial module can be
found in sys.h, sys.c, stdio.h, and stdio.c. The queued serial
module is not curently built into kernel in any way. This is a
planned improvement.
- `tpu_control_ptr tpu_control;'
-
Pointer to the timer processor unit configuration and control
registers.
Some examples are:
tpu_control->cier - channel interrupt enable register
tpu_control->tmcr.stop - bit for stopping the entire TPU
Functions for controlling the TPU are found in TPU.h and TPU.c.
Actual memory map structures:
typedef volatile struct simmap {
word mcr;
word simtr;
struct syncr_struct {
uint w :1;
uint x :1;
uint y :6;
uint ediv :1;
uint :2;
volatile uint slimp :1;
volatile uint slock :1;
uint rsten :1;
uint stsim :1;
uint stext :1;} syncr;
byte u1; byte rsr;
word simtre;
uint u2;
word u3;
byte u4; volatile byte porte;
byte u5; volatile byte eport;
byte u6; byte ddre;
byte u7; byte pepar;
byte u8; volatile byte portf;
byte u9; volatile byte fport;
byte u10; byte ddrf;
byte u11; byte pfpar;
struct sypcr_struct {
uint u12 :8;
uint swe :1; /* watchdog enable */
uint swp :1; /* watchdog prescale */
uint swt :2; /* watchdog timing */
uint hme :1; /* halt monitor enable */
uint bme :1; /* bus monitor enable */
uint bmt :2; /* bus monitor timing */
} sypcr; /* system protection ctrl register */
struct picr_struct {
uint :5;
uint pirq:3;
uint piv :8;} picr;
struct pitr_struct {
uint :7;
uint ptp :1;
uint pitr:8;} pitr;
byte u13; byte swsr;
word ua28;
word ua2a;
word ua2c;
word ua2e;
word tstmsra; /* a30 */
word tstmsrb;
byte tstsca; byte tstscb;
word tstrc;
word creg;
word dreg;
uint u15;
byte u16; byte cspdr;} *simptr;
extern const simptr sim;
struct chip_select_channel {
uint addr :13;
uint blksz :3;
uint mode :1;
uint byte :2;
uint rw :2;
uint strb :1;
uint dsack :4;
uint space :2;
uint ipl :3;
uint avec :1;};
typedef struct chip_select_map {
struct cspar_struct {
uint :2;
uint cs5 :2;
uint cs4 :2;
uint cs3 :2;
uint cs2 :2;
uint cs1 :2;
uint swsr1 :2;
uint swsr2 :2; /* end of first word */
uint :6;
uint cs10 :2;
uint cs9 :2;
uint cs8 :2;
uint cs7 :2;
uint cs6 :2;} cspar;
struct chip_select_channel boot;
struct chip_select_channel csch[10];} *csmptr;
extern const csmptr csm;
struct synch_ser_comnd {
uint cont :1;
uint bitse :1;
uint dt :1;
uint dsck :1;
uint psc3 :1;
uint psc2 :1;
uint psc1 :1;
uint psc0ss :1;};
typedef volatile struct queued_serial_module {
struct qmcr_struct {
uint stop :1;
uint frz1 :1;
uint frz0 :1;
uint :5;
uint supv :1;
uint :3;
uint iarb :4;} qmcr;
word qtest;
struct qilr_struct {
uint :2;
uint ilqspi :3;
uint ilsci :3;
uint qivr :8;} qir;
word r0;
word scbr; /* baud control register */
struct sccr_struct {
uint :1;
uint loops :1;
uint woms :1;
uint ilt :1;
uint pt :1;
uint pe :1;
uint m :1;
uint wake :1;
uint tie :1;
uint tcie :1;
uint rie :1;
uint ilie :1;
uint te :1;
uint re :1;
uint rwu :1;
uint sbk :1;} sccr;
volatile struct scsr_struct {
uint :7;
volatile uint tdre :1;
volatile uint tc :1;
volatile uint rdrf :1;
volatile uint raf :1;
volatile uint idle :1;
volatile uint or :1;
volatile uint nf :1;
volatile uint fe :1;
volatile uint pf :1;} scsr;
volatile word scdr;
uint r1;
byte r2; byte qpdr;
union qpar_qddr_union {
struct qpar_struct {
uint :1;
uint pcs :3;
uint pcs0ss :1;
uint :1;
uint mosi :1;
uint miso :1;
uint :8;} qpar;
struct qddr_struct {
uint :8;
uint txd :1;
uint pcs :3;
uint pcs0ss :1;
uint sck :1;
uint mosi :1;
uint miso :1;} qddr;
} qpar_qddr;
struct spcr0_struct {
uint mstr :1;
uint womq :1;
uint bits :4;
uint cpol :1;
uint cpha :1;
uint spbr :8;} spcr0;
struct spcr1_struct {
uint spe :1;
uint dsckl :7;
uint dtl :8;} spcr1;
struct spcr2_struct {
uint spifie :1;
uint wren :1;
uint wrto :1;
uint :1;
uint endqp :4;
uint :4;
uint newqp :4;} spcr2;
struct spcr3_spsr_struct {
uint :5;
uint loopq :1;
uint hmie :1;
uint halt :1;
uint spif :1;
uint modf :1;
uint halta :1;
uint :1;
uint cptqp :4;} spcr3_spsr;
byte r3[0xe0];
word rec_ram[16];
word tran_ram[16];
byte comd_ram[16];} *qsmptr;
extern const qsmptr qsm;
typedef struct tpu_control_struct {
struct tmcr_struct {
uint stop :1;
uint tcr1_prescale :2;
uint tcr2_prescale :2;
uint emu :1;
uint t2cg :1;
uint stf :1;
uint supv :1;
uint psck :1;
uint :2;
uint interrupt_arbitration :4;} tmcr;
word ttcr;
word dscr;
word dssr;
struct ticr_struct {
uint :5;
uint interrupt_request_level :3;
uint interrupt_base_vector :8;} ticr;
word cier;
word cfs[4];
word hsf[2];
volatile word hsrf[2];
word cpr[2];
volatile word cisr;} *tpu_control_ptr;
extern const tpu_control_ptr tpu_control;
The
- `int period_of(int hz);'
-
Given rate in hertz, period_of returns period in ns
- `int rate_of(int ns);'
-
Given period in nanoseconds, rate_of returns rate in hertz
- `void set_porte_output(byte mask);'
-
- `void set_porte_input(byte mask);'
-
Sets port E lines masked as 1s in argument to output/input
- `int read_porte();'
-
Returns the current value on port E data pins. The bits
only have meaning if they are configured to port E input.
- `void write_porte(byte data);'
-
Writes the data to port E data pins. Only has effect for
bits configured for port E output
- `void set_porte_bus_control(byte mask);'
-
Sets masked port E lines to alternate bus control functions
- `void set_portf_output(byte mask);'
-
- `void set_portf_input(byte mask);'
-
Sets port F lines masked as 1s in argument to output/input
- `int read_portf();'
-
Returns the current value on port F data pins. The bits
only have meaning if they are configured to port F input.
- `void write_portf(byte data);'
-
Writes the data to port F data pins. Only has effect for
bits configured for port F output
- `void set_portf_interrupt_request(byte mask);'
-
Sets masked port F lines to alternate interrupt functions
- `** In theory, the port C functions should work, but I have not really'
-
tested them yet. Port C coexists with the chip selects, so be
very careful in using these functions, as bashing the chip
selects on the ROM or RAM will crash you right away.
- `void set_portc_output(byte mask);'
-
Sets masked port C lines to output function. The MSB
when configured for output becomes the Eclock. The other
bits are normally chip selects and can be configured for
discrete output.
- `void write_portc(byte data);'
-
Writes the data to port C data pins. Only has effect for
bits configured for port C output and never for the MSB.
- `int clock_rate();'
-
Returns the current system clock rate in hertz. This
function is memoized so calculation is not usually necessary
so don't stress about using this function.
- `int clock_period();'
-
Returns the current system clock period in nanoseconds.
- `int set_clock_rate(int speed);'
-
Set_clock_rate takes an argument in hertz, sets the clock to
the closest possible approximation of that speed, and returns
the clock rate actually set (in hertz).
- `void wait_clock_lock();'
-
This probably isn't practical, but after a clock rate change
this will wait until the clock source has locked onto the new
speed after a clock speed change.
enum cs_pin {output,default_config,cs_8bit,cs_16bit};
enum cs_size {s2k,s8k,s16k,s64k,s128k,s256k,s512k,s1m};
enum cs_mode {async,sync};
enum cs_byte {off,lower,upper,both};
enum cs_rw {read=1,write,rw};
enum cs_strobe {as,ds};
enum cs_space {cpu,user,supervisor,u_s};
enum cs_avec {avec_off,avec_on};
- `int initialize_chip_select(int num,enum cs_pin pin,void *base_addr,'
-
enum cs_size size,enum cs_mode mode,
enum cs_byte byte_config,
enum cs_rw rw,enum cs_strobe strobe,
byte dsack_delay,enum cs_space space,
byte interrupt_level,
enum cs_avec avec);
Configures the chip select num to the options provided.
Num may range from 0 to 10 (returns -1 if out of range),
but on the Motorola bcc board uses chip selects 0, 1 and 2
and the Vesta board uses chip selects 0 and 1 for main ram.
The cs_pin argument should only be either cs_8bit for
8 bit transfers or cs_16bit for 16 bit transfers.
Dsack_delay can be from 0 to 13 wait states per access
(with 14 meaning it can go real fast and 15 meaning external ack),
and interrupt_level from 1 to 7 (with 0 meaning all interrupts
allowed). The other parameters should be self explanatory from
the enums listed above, and for details of operation refer to
the 68332 user's manual pages 4-27 to 4-45. The format of
this instruction corresponds directly to the format of
table 4-18 on page 4-38 of the text.
After configuring a chip select using this function, any
reference to the block of memory starting at the base_address
(which must lie on an appropriate boundary) and the block
of memory the size of which is specified by size will cause
the appropriate chip select to give your peripheral an active
low chip select signal telling it to either drive the bus
if the operation is a read or take data from the bus if the
operation is a write. This effectively memory maps your
external device for you!
- `void set_dsack(int cs,int val);'
-
This function will set the dsack for the specified chip select
to the specified number of wait states. See above for
description of the numbers.
- `int set_cs_size(int cs,int val);'
-
Assuming that the chip select cs is already configured, this
function will set the block size for the chip select to the
smallest block size greater than or equal to the length in
bytes specified by val. The value returned is the actual
length in bytes that the chip select was set to. Val should
be a power of 2, but enven then only a limited set of lengths
are possible.
The possible lengths and their representations in
hexidecimal are as follows:
2k = 0x800 16k = 0x4000 128k = 0x20000 512k = 0x80000
8k = 0x2000 64k = 0x10000 256k = 0x40000 1M = 0x100000
- `int baud_rate();'
-
Returns the current baud rate of the 68332.
- `int set_baud_rate(int baud);'
-
Given a nominal baud rate to shoot for, this will set the
asynchronous baud rate to as close to that as possible and
return the exact baud as set (usually some error necessary).
- `int recalibrate_baud();'
-
You should not ever need to call this as you should only
change the clock speed by calling set_clock_rate which
does this automatically, but this function will recalibrate
the system to maintain a stable baud rate despite changes
in system clock speed.
This module includes functions for affecting the Timer Processor Unit
as a whole, and general functions for affecting the actions of each pin.
For all the functions which take a channel number as an argument, the
channel number should be in the range 0 to 15, which correspond directly
with the TPU pin number. Therefore pin TPU0 corresponds to an argument
0, TPU15 to 15, etc. This argument could also be channel_of(ptr) where
ptr is the handle returned from an initialization function on a channel.
- `#define channel_of(ptr) {blah...}'
-
Given a pointer to the parameter space of a TPU function
(which is what the initialization functions return)
this will tell you what channel that pointer corresponds
to.
Here is the intended use:
If you had a pwm channel that you initialized by:
pwm pptr=pwm_init(blah blah);
that you wanted to disable temporarily (or call any of the
general TPU functions that require a channel as an argument),
you could use:
set_timer_priority(channel_of(pptr),0);
and not have to keep track of what channel it was initialized on.
This operation is quite efficient (it only requires bit masking,
not any sort of table lookup), and I recommend using it for
better code readability.
These functions affect the configuration and operation of the TPU as a whole.
- `void stop_tpu();'
-
Stops the TPU. No channels are serviced. The TCRs don't
increment. It's dead, Jim.
- `int tpu_stopped();'
-
Returns 1 if TPU is stopped, 0 if not.
- `void start_tpu();'
-
Restarts the TPU after it has been stopped.
- `void initialize_tpu_interrupts();'
-
Calling this function configures the TPU to allow it to
trigger CPU interrupts. This function is automatically called
by kernel, so you should not need to call it.
- `void disable_tpu_interrupts();'
-
This will uniformly prevent the TPU interrupts from being
acknowledged by the CPU in case you don't want it to bother
you for some amount of time...
- `void enable_tpu_interrupts();'
-
This will undo the effect of the previous function and reenable
TPU interrupts
The time base for the TPU channel functions can either be TCR1 or TCR2.
Both are 16-bit free-running counters. TCR1 is always a divisor of the
system clock. TCR2 is affected by the pin T2CLK. These functions
affect the TCRs.
enum psck_enum {psck_div32,psck_div4};
enum tcr_prescale {div1,div2,div4,div8};
enum tcr2_pin_use {pin_is_clock_source,pin_is_gate_of_div8_clock};
typedef enum tcr_source_enum {tcr1= 1, tcr2= 2} tcr_source;
- `int initialize_tpu_speeds(enum psck_enum psck,enum tcr_prescale t1p,enum tcr_prescale t2p);'
-
It is possible to set the divisor from the system clock
(psck), the prescales from that clock source to TCR1, and the
prescale for TCR2 **ONLY ONCE BETWEEN RESETS**. Worse than
that, the same register that controls the the prescales also
controls the interrupt arbitration id, which must be set
during kernel initialization. Therefore, the first time the
user try to change the prescales it will not work.
The most recently requested prescale values are stored in
persistents, and at the next reset the values will be set
correctly. initialize_tpu_speeds will return 1 if the values
actually in the register match the requested values, and 0 if
they don't.
The best thing to do if this returns 0 is to request that the
user hit reset, and it will be set right the next time. The
system software specifically does not try to set the
prescales, and you should make sure that your code either
tries to set it only once, or that all places which try to set
the prescales agree on what they should be. YOU SPECIFICALLY
*CANNOT* CHANGE THESE VALUES DYNAMICALLY.
The following table gives the results of setting the prescales
for a 16.7MHz system clock. If the system clock is a different
speed, scale these numbers accordingly.
TCR1 prescale | psck=psck_div32 | psck=psck_div4 |
|div from sys | speed | div from sys | speed |
div1 | 32 ** | 2us | 4 | 250 ns |
div2 | 64 | 4us | 8 | 500 ns |
div4 | 128 | 8us | 16 | 1 us |
div8 | 256 | 16us | 32 | 2 us |
TCR2 prescale | divide by | div from sys | div from external |
div1 | 1 ** | 8 | 1 |
div2 | 2 | 16 | 2 |
div4 | 4 | 32 | 4 |
div8 | 8 | 64 | 8 |
- `void set_tcr2_source(enum tcr2_pin_use);'
-
- pin_is_clock_source
Sets the source for TCR2 to be the signal
at the pin T2CLK.
- pin_is_gate_of_div8_clock
Sets the source for TCR2 to be the system clock divided by 8 and gated
by the pin. In other words, if the system clock is 16MHz and you want
TCR2 to go at 2MHz, use this option and tie T2CLK to VCC. If you want
TCR2 to accumulate pulse widths of a signal, hook that signal to TCR2
and the TCR2 count * .5us is the accumulated time.
- `int calculate_tcr1_speed();'
-
You shouldn't need to call this as it is automatically
called if you change the clock speed or prescale using the
functions provided. However, it pre-calculates and stores
the period and rate of TCR1 and returns the rate in hertz.
- `int tcr1_rate();'
-
Returns TCR1 speed in hertz.
- `int tcr1_period();'
-
Returns TCR1 period in nanoseconds.
- `int period_to_tcr1_counts(int period,int *error);'
-
Returns the number of TCR1 counts that comes closest to while
not exceeding the period argument in nanoseconds and stores
the error in nanoseconds of the resulting value in *error.
TCR1 is only a 16-bit counter, so you should check that the
value returned is less than 65000 before using it for TPU
functions. If the value is too large, consider setting the
TCR1 prescale and/or PSCK to values that cause TCR1 to run
slower. See `initialize_tpu_speeds()'.
- `int rate_to_tcr1_counts(int hertz,int *error);'
-
Same as above, but argument is in hertz. Error is still in
nanoseconds. The same warnings about overrunning 16 bits
apply as above.
- `int tcr1_counts_to_period(int counts);'
-
Returns the period in nanoseconds corresponding to the
given number of TCR1 counts.
- `int tcr1_counts_to_rate(int counts);'
-
Returns the rate in hertz corresponding to the
given number of TCR1 counts.
All built-in TPU functions require the channel control field to be set
as part of channel initialization. The channel control structure
actually consists of three fields which affect the TPU hardware latches
associated with each pin -- pin state control, pin action control, and
time base/directionality seletion.
The actual types used to map on to the channel control field of the TPU
register are as follows. The reason a union is used rather than just a
plain struct is that you may want to load all the fields at once for
efficiency (and therefore use the `w' part), or you may want to
load each field inividually without worrying about shifts and masks (and
therefore use the `bf' part).
typedef union {
word w;
struct {
uint :7;
uint tbs:4;
uint pac:3;
uint psc:2;} bf;} channel_control;
enum pac_input {no_detect_edge,rising_edge,falling_edge,either_edge};
enum tbs_input {i_c1_m1,i_c1_m2,i_c2_m1,i_c2_m2};
enum psc_output {follow_PAC,force_high,force_low,no_force};
enum pac_output {no_change,high_on_match,low_on_match,
toggle_on_match,preserve_pac};
enum tbs_output {o_c1_m1=4,o_c1_m2,o_c2_m1,o_c2_m2,preserve_tbs};
Pin State Control (psc)
Controls the immediate action of a pin in response to initialization.
This field only affects output channels (and therefore is not even
an argument to the function to set the channel control of an
input). The values are as follows:
- `follow_PAC'
-
Do what the Pin action control field specifies
- `force_high'
-
Force output high on initialization
- `force_low'
-
Force output low on initialization
- `no_force'
-
Leave the pin state as you found it
Pin Action Control:
Controls the action of a pin in response to a match or transition.
The meaning of this field is affected by whether the pin is an input or
output.
- Input
- `no_detect_edge'
-
Do not do anything when a transition occurs
- `rising_edge'
-
Capture rising edges
- `falling_edge'
-
Capture falling edges
- `either_edge'
-
Capture rising and falling edges
- `preserve_pac'
-
Leave the PAC at the value it had last time
- Output
- `no_change'
-
Do not change the output level when a match occurs
- `high_on_match'
-
Set the output to be high when a match occurs
- `low_on_match'
-
Set the output to be low when a match occurs
- `toggle_on_match'
-
Toggle the output when a match occurs
- `preserve_pac'
-
Leave the PAC at the value it had last time
Time Base/Directionality Selection (tbs)
Controls whether the channel is input or output, and whether
it uses TCR1 or TCR2. It is actually possible to match on and capture
different TCRs, but in general you want to use the same TCR for both.
- Input
- `i_c1_m1'
-
Input capture TCR1, match TCR1
- `i_c1_m2'
-
Input capture TCR1, match TCR2
- `i_c2_m1'
-
Input capture TCR2, match TCR1
- `i_c2_m2'
-
Input capture TCR2, match TCR2
- `preserve_tbs'
-
Leave TBS as it was
- Output
- `o_c1_m1'
-
Output capture TCR1, match TCR1
- `o_c1_m2'
-
Output capture TCR1, match TCR2
- `o_c2_m1'
-
Output capture TCR2, match TCR1
- `o_c2_m2'
-
Output capture TCR2, match TCR2
- `preserve_tbs'
-
Leave TBS as it was
Channel Control functions
- `channel_control channel_control_input(enum pac_input pac,enum tbs_input tbs);'
-
- `channel_control channel_control_output(enum psc_output psc,enum pac_output pac,enum tbs_output tbs);'
-
These functions return the value of the channel_control
parameter required for initializing TPU functions. These
functions take their arguments and pack them appropriately
to insert into the TPU parameter ram or pass to channel
initialization functions. Example of using these functions
follow:
- Input
channel_control ctl=channel_control_input(either_edge,i_c1_m1);
din=dio_input_init(ch,100,transition,3,ctl);
- Output:
channel_control ctl =
channel_control_output(force_low,toggle_on_match,o_c1_m1);
sqwave=pwm_init(15,1000,2000,1,ctl);
- `void install_tpu_exception_handler(int ch,void *p);'
-
This function installs the given function as the interrupt handler
for the given channel. This DOES NOT enable the
interrupts on the channel and should be called before
enabling the interrupts on a channel.
- `void set_timer_interrupt_enable(int ch,int val);'
-
This function enables or disables interrupts on the given TPU
channel. If val==0 the given channel will not interrupt the CPU,
when an interrupt-inducing event occurs (transition, match, whatever),
but will instead just set a bit in the timer interrupt status field
(which can be polled using timer_interrupt_status). If val==1,
then exception handler previously installed by a call to
install_tpu_exception_handler() will be called when an interrupt
occurs.
- `int timer_interrupt_status(int ch);'
-
This is useless if the interrupts are enabled on the given
channel. If interrupts are disabled, timer_interrupt_status
will return 0 if no interrupt has occurred, or 1 if an
interrupt has occured. After reading that an interrupt has
occurred, you must clear the status flag by a call to
reset_timer_interrupt. Interrupts are disabled by default, or
by calling set_timer_interrupts_enable(ch,0) at any time.
- `void reset_timer_interrupt(int ch);'
-
If interrupts are disabled, use this to reset the interrupt
status bit after to allow polling for a new interrupt. If
interrupts are enabled, you can call this from your interrupt
routine to reset for the next interrupt. Note that if you
are using intstall_tpu_exception_handler, you do not need
to call this in your interrupt as the status bit is reset
automatically. However, it doesn't hurt to call it anyway.
If you are using your own interrupt wrapper, you should call
definitely call this, as failing to will cause the
interrupt to be repeatedly handled and prevent other processing
from happening.
- `int wait_for_timer_interrupt(int channel);'
-
*** Only use this function on channels for which interrupts
are disabled!! ***** This function will loop until the status
bit of the given channel is set, then it will reset the status
bit and return.
- `void set_timer_priority(int ch,int val);'
-
Sets the timer priority for the given channel to val where
val is in the range of 0 (disable service to the channel) to
3 (this is a really important channel, service promptly).
See the Motorola TPU reference manual for more details about
how priority affects service latency.
- `int wait_for_host_service_complete(int channel);'
-
The CPU communicates with the TPU through shared parameter
space. Therefore when the CPU requests service from the TPU
there may be some delay before the request is completed. Call
this function if you need to wait until a service request has
been completed before continuing with program execution. In
most cases this is not necessary. It is only necessary when
synchronization between the CPU and TPU is required.
Please note the following general things about using channel functions:
- The channel_control argument to all channel functions should be
created using channel_control_input() or channel_control_output().
See the section on channel control above for details.
- The channel control field of a channel functions parameter space is
only used during initialization. If you want to change the channel
control (for instance, change a rising-edge channel into a falling-
edge channel), you MUST reinitialize the channel, or at least issue
a host service request.
- The channel argument (ch) must always be in the range of 0 to 15.
- If a channel that was previously configured for one function is
subsequently re-initialized, only the most recent initialization matters.
- The pointers (or you could perhaps better think of them as objects)
returned by the initialization functions can be used as a pointer
into the parameter space for the channel. For example, if foo is
an object of type dio returned by the dio initialization function,
then
foo->pin_level
is the pin level of that tpu channel.
- You can recover the channel from a tpu function pointer by using
channel_of(ptr)
.
- Priority is a number from 0 (lowest priority=disable) to 3
(highest priority) that is used to arbitrate simultaneous
servicing requests.
- If you wish to enable interrupts on a channel, the appropriate ordering
of the initialization steps is as follows:
- Initialize channel using an initialization function like
pwm_init,itc_init, dio_output_init,
etc.
- Call install_tpu_exception_handler to specify which function to
execute on interrupt.
- Call set_timer_interrupt_enable when you want to enable interrupt.
Executing these steps out of order can lead to serious problems.
To use these functions you must `#include "tpu/dio.h"'.
typedef struct dio_struct {
channel_control ctl;
volatile word pin_level;
word match_rate;} *dio;
enum dio_hsf {transition=0,match=1,cpu_sample=3};
- `dio dio_output_init(int ch,int state);'
-
Initializes the channel to discrete output with initial
value state (either 1=HIGH or 0=LOW). Subsequent changes
to the output level can be made by calling dio_out on the
initialized channel.
- `void dio_out(dio dptr,int state);'
-
Set pre-initialized discrete output channel to given state.
- `dio dio_input_init(int ch,int match_rate,enum dio_hsf hsf,int priority,channel_control ctl);'
-
Initializes the channel to discrete input, updating either
on input transition, a a constant rate match_rate (this
parameter is meaningless if hsf!=match...), or when
the cpu requests update.
- `int dio_state(dio dptr);'
-
Returns a word (lower two bytes of int...) corresponding to
the last 16 states of the input channel.
- `int dio_now(dio dptr);'
-
Returns 0 if current (or most recently sampled) discrete
input state is low and non-zero (actually 0x8000 'cause
it's cheap) otherwise.
To use these functions you must `#include "tpu/pwm.h"'.
enum pwm_hsrf {pwm_initialize=2,pwm_update=1};
typedef struct pwm_struct {
channel_control ctl;
volatile word oldris;
word pwmhi;
word pwmper;
volatile word pwmris;
word slate;} *pwm;
- `pwm pwm_init(int ch,int hightime,int period,int priority,channel_control ctl);'
-
Initializes the channel to pulse width modulated output with the
given period and hightime (where hightime/period=duty cycle of the
wave).
- `void pwm_set_times(pwm pptr,int high,int per);'
-
Changes the high time and period on an initialized pwm channel
to the given values.
- `void pwm_disable(pwm pptr);'
-
- `void pwm_enable(pwm pptr,int priority);'
-
pwm_disable really just sets the channel priority regiter to
0, but results in suspending the output of a pwm channel without
otherwise disturbing it. pwm_enable undoes this hold and
assigns the channel the given priority.
To use these functions you must `#include "tpu/itc.h"'.
typedef struct itc_struct {
channel_control ctl;
volatile word lb;
word max_count;
volatile word trans_count;
volatile word final_trans_time;
volatile word last_trans_time;} *itc;
enum itc_hsf {single_no_links,continual_no_links,
single_links,continual_links};
- `itc itc_init(int ch,int max_cnt,enum itc_hsf hsf,int priority,channel_control ctl);'
-
Initializes a channel for input timer capture/transition count.
Note that links are not yet supported in this version of the
libraries, so the valid values of hsf are single_no_links for
one-shot action and continual_no_links for continuous action.
- `void set_itc_max_cnt(itc iptr,int cnt);'
-
Allows the maximum number of counts for an initialized itc
channel to be reset.
- `int get_itc_last_period(itc iptr);'
-
Returns the last period accumulated by an initialized itc channel.
- `int reset_itc_counts(itc iptr);'
-
Resets the number of counts an itc channel has accumulated
To use these functions `#include "tpu/oc.h"'.
This is unbelievably complex. I don't fully understand it yet.
I'll document output compare when I understand it...
typedef struct oc_struct {
channel_control ctl;
word offset;
word rat_ref1;
word ref2_3;
volatile word ref_time;
volatile word actual_match_time;} *oc;
enum oc_hsrf {host_match=1,continuous_pulse=3};
enum oc_hsf {all=0,TCR_only=2};
#define TCR1 0xec
#define TCR2 0xee
#define TCR1_time() ((int)*(word *)0xffffec)
#define TCR2_time() ((int)*(word *)0xffffee)
#define timeptr_of(p) (((word)(p)) & 0xff)
oc oc_init(int ch,word timeptr,int priority,channel_control ctl);
oc oc_pulse_init(int ch,word timeptr,int width,int priority);
void oc_initiate(oc optr,int width);
int oc_timecalc(oc optr,int timeptr);
To use these functions you must `#include "tpu/ppwa.h"'
typedef struct ppwa_struct {
channel_control ctl;
word max_period_cnt;
volatile word last_accum;
volatile word accum;
volatile word accum_rate_ub;
volatile word ppwa_lw;} *ppwa;
enum ppwa_hsf {period_24=0,period_16,pulse_24,pulse_16};
- `ppwa ppwa_init(int ch,int max_counts,int accum_rate,int priority,enum ppwa_hsf hsf,channel_control ctl);'
-
Initializes the channel to execute pulse width or period
accumulation for max_counts waveforms between interrupts
and place the finished results in its parameter space.
For consistency, if initializing ppwa channels for period measurement,
the pac field of channel_control should be to detect falling edges
and for pulse width measurement it should be to detect rising edges.
- `void ppwa_set_accum_rate(ppwa pptr,int val);'
-
Allows resetting of the accum_rate for an initialized ppwa channel.
- `void ppwa_reset_high_byte(ppwa pptr);'
-
For 24bit measurement, the high byte does not automatically get
reset so you can use this function to do that.
- `void ppwa_set_max_counts(ppwa pptr,int val);'
-
Allows resetting of the max_count for an initialized ppwa channel.
- `int ppwa_full_period(ppwa pptr);'
-
Returns the full period for 24bit accumulation. This function
does not, however, clear the upper byte of the accumulator
so be careful to use this in concert with ppwa_reset_high_byte().
- `int ppwa_ave_period(ppwa pptr);'
-
Divides the accumulated periods/pulse widths by the number of
counts it represents and returns that value (rounded to an
integer...).
- `int ppwa_last_pulse_time(ppwa pptr);'
-
Returns the last TCR1 time at which a pulse was detected.
Go to the first, previous, next, last section, table of contents.