OS - A Real Time Operating System for the Motorolla MC68HC11
Document Version $Id: os.doc,v 1.2 2002/07/24 14:51:42 tomdean Exp $

OS is a simple round-robbin time slice operating system.  OS has a fixed number of tasks, eight.

Contents:

File Versions

Files With Links to Source Code

LCD Connections

Context Switch Stack

GPS Data

Observations

Tar File (http://www.speakeasy.net/~tomdean/c-source/os.tgz)



File Versions.

Makefile Makefile,v 1.6 2002/07/30 22:36:45 tomdean
adc.c adc.c,v 1.5 2002/07/31 05:52:16 tomdean
cli.c cli.c,v 1.5 2002/07/31 17:29:47 tomdean
display.c display.c,v 1.5 2002/07/31 16:50:57 tomdean
lcd.h lcd.h,v 1.3 2002/07/31 05:52:16 tomdean
main.c main.c,v 1.9 2002/07/31 05:52:16 tomdean
os.c os.c,v 1.10 2002/07/31 17:29:47 tomdean
os.h os.h,v 1.12 2002/07/31 17:29:47 tomdean
rti.c rti.c,v 1.6 2002/07/31 17:29:47 tomdean
sci.c sci.c,v 1.7 2002/07/31 17:29:47 tomdean
swi.c swi.c,v 1.9 2002/07/31 16:50:57 tomdean
task1.c task1.c,v 1.6 2002/07/30 22:36:45 tomdean
task2.c task2.c,v 1.6 2002/07/31 05:52:16 tomdean
task4.c task4.c,v 1.5 2002/07/31 05:52:16 tomdean
task5.c task5.c,v 1.7 2002/07/31 17:29:47 tomdean
task7.c task7.c,v 1.5 2002/07/31 05:52:16 tomdean
 



Files

os.h - System Definitions <Source Code>

  1. Function Prototypes
  2. Structures
  3. Defines

os.c - system things. <Source Code>

  1. Task init sets up task initial stack.
  2. Add handler adds an interrupt handler.
  3.  Clear handler sets the specified interrupt handler to dismiss, by setting the opcode to 'rti' and the task to zero.

os.h - system things. <Source Code>

main.c - The main routine.  <Source Code>

  1. Everything starts here.
  2. Contains the null task.
  3. Rti is enabled, but, at this time, interrupts are disabled.
  4. All interrupts are set to dismiss.
  5. The respective task contexts are connected to the context array.
  6. Each of the eight tasks are added.
  7. Interrupt handlers for the sci, rti, and,  swi are installed.
  8. The current task is set to zero, the null  task.
  9. All the timers are set to 0xff for testing.
  10. Tocks is  set to TICKS_PER_TOCK.
  11. Interrupts are enabled.
  12. Then, we enter the null task.  The null task loops, setting porta, bit 0x20 and clearing it.   Makes a good scope point.  Some debug/testing things are in   between.  A null task counter should be added for calculating  system time availability.
  13. A __premain() function is defined  at the bottom.  This is a work-around for m6811-elf-gcc  3.0.4's __premain() enabling interrupts before we are ready!  When linking, __premain() is already defined, so, the library  is not searched.  

rti.c - rti interrupt handler. <Source Code>

  1. Context switches and clock.
  2. THIS FUNCTION REQUIRES A SPECIFIC STACK CONFIGURATION.  WHEN MAKING  CHANGES TO RTI.C, LOOK AT THE .s OR USE 'm6811-elf-objdump -d'  TO CHECK FOR STACK PUSH OR SOFT REGISTER USAGE BEFORE THE SOFT  REGISTERS ARE SAVED.  VIOLATION OF THE STACK CONTENTS WILL CAUSE STRANGE THINGS TO HAPPEN.  MOST LIKELY A COMPLETE FAILURE OF OS.
  3. See rti.c for a description of the clockalgorithm.
  4. Porta, bit 0x80 is set.
  5. Tocks is decremented, and if zero, it is set to TICKS_PER_TOCK and a context switch is begun.
  6. The top half of a context switch consists of making the stack have the proper format.  See rti.c or swi.c for the stack format.
  7. An interrupt may occur in the middle of a 'C' statement, at 68hc11 instruction bounaries.  Therefore, the rti() function preamble saves some of the context.
  8. Then, the clock is adjusted.  This takes 300 to 320 usec.
  9. If we are doing a  context switch, the sp is pointed to the next task.  The stack  is unwound and the following rti changes to the next task.
  10. The context switch takes about 80 usec.

swi.c - syscall. <Source Code>

  1. May perform a task switch.
  2. THIS FUNCTION REQUIRES A SPECIFIC STACK CONFIGURATION.  WHEN MAKING  CHANGES TO SWI.C, LOOK AT THE .s OR USE 'm6811-elf-objdump -d'  TO CHECK FOR STACK PUSH OR SOFT REGISTER USAGE BEFORE THE SOFT  REGISTERS ARE SAVED.  VIOLATION OF THE STACK CONTENTS WILL CAUSE STRANGE THINGS TO HAPPEN.  MOST LIKELY A COMPLETE FAILURE OF OS.
  3. Syscall() performs an swi, starting the swi() function.  Return from the swi() function is by an rti.  So, the swi is a synchronous interrupt. Synchronous to the previous code stream.  An swi() is a full 'C' statement, so it is not in the midst of a statement.  Less of the context is saved than for an interrupt.  Therefore, swi() must manipulate the stack, somewhat do it is identical to the interrupt stack.
  4. Arguments may be passed. 
  5. System operations for the first implementation of swi() will be
  • SYS_WAIT_TIMER(n).  Wait timer (tocks).  A tock is 4.1 msec.
  • SYS_STOP(t).  Mark task (n) as not ready to run.
  • SYS_START(t).  Mark task (n) as ready to run.
  • SYS_DONE().  This iteration of current task is finished, do a context switch to the next ready task.
  1. Near future implementations may include
  • SYS_SEND(t, m).  Send message to task (n).
  • SYS_RECV(m).     Recveive message.
  • (Not Needed) SYS_QUEUE(t, m).  Queue mesage to task (n).
  • (Not Needed) SYS_DEQUEUE(m).  Dequeue message.

sci.c - serial line handler <Source Code>

The SCI handler does both receive and transmit functions for the serial line.  The handler is entirely interrupt driven.

When an interrupt happens, the handler first checks for character received and handles that.  Then, the transmit buffer is checked.  If the buffer is not empty, the next character is sent.

When a character is received, it is put in the receive buffer queue.  Some character translations occur.  0x0d is translated to 0x00 and put in the buffer.  0x0a is translated to ox00 and put in the buffer, and, eol is handled.

To transmit the first character is sent and interrupts are enabled.

The sci handler has three routines.

sci().

The sci handler interrupt routine.  Handle receive characters first to possibly avoid overrun.
  1. If not a character received interrupt, goto 7
  2. get the character from the device.  Note:  #1 and #2 ack the interrupt
  3. if 0x0d translate to 0x00,  put in the buffer and goto 6
  4. if 0x0a, translate to 0x00, put in the buffer
  5. send signal to the cli task
  6. rti
  7. get the next tx buffer
  8. if the char is 0x00, goto 11
  9. send tha char
  10. goto 14
  11. read the scdr to make sure interrupts are ack'ed
  12. mark the tx buffer as empty
  13. disable transmitter and related interrupts
  14. rti

Send().

Called by the os print function, the transmitter is enabled, along with the transmit related interrupts.
  1. put the buffer in the tx queue
  2. enable transmitter and transmit related interrupts
  3. send first char

Sci_init().

Called to initialize the sci device and the sci buffers.
  1. initialize the sci buffers
  2. enable receiver and receive related interrupts.

task1.c - task one. <Source Code>

  1. For now, just send the task number up the sci.

task2.c - task two. <Source Code>

  1. For now, just send the task number up the sci.

adc.c - task three. <Source Code>

  1. Read each of the adc's and put on row 1 of the lcd.  Position of each adc is conteolled by defines in os.h.

gps.c - task four. <Source Code>

  1. Recognize and count gps messages.

gps.h - defines for gps.c<Source Code>

task5.c - task five. <Source Code>

  1. For now, just send the task number up the sci.

cli.c - task 6.  Command line interpreter. <Source Code>

Parse incomming buffers and pass to the proper handler.

task7.c - task seven. <Source Code>

  1. For now, just send the task number up the sci.

display.c - task eight.  Display task. <Source Code>

Display task.  Copy the lcd display buffer to the lcd.  There is one buffer for each lcd row.  Each task is responsible for formatting its data into the correct lcd buffer at the correct location.  Locations are defined in lcd.h

lcd_init() - initialize the lcd display.  This takes a very long time, asround 5 msec, so, it is called before interrupts are enabled.  Do not call this routine during normal os operation.

  1. write 0x30, delay 4.1 msec 
  2. write 0x30, delay 100 usec
  3. write 0x30, delay 37 usec
  4. write 0x3c, delay 37 usec
  5. write 0x08, delay 37 usec
  6. write 0x01, delay 37 usec
  7. write 0x06, delay 4.1 msec <-- determined by trial and error, this may be too long.
  8. write 0x0c, delay 37 usec
_lcd_read_ctlr1
_lcd_read_ctlr2
_lcd_write_both
_lcd_write_ctlr1
_lcd_write_ctlr2
_lcd_write_row_1
_lcd_write_row_2
_lcd_write_row_3
_lcd_write_row_4

lcd.h - defines related to the lcd.<Source Code>

Makefile.  Build OS. <Source Code>


Stack contents for a context switch.

  1. All task stacks have this format.
BE VERY CAREFUL IN CHANGING rti() or swi().  SOME CODE WILL FORCE SAVING SOFT REGISTERS OR MAKING ROOM VOR VARIABLES ON THE STACK.  IF THIS HAPPENS, THE CONTEXT STRUCTURE WILL BE VIOLATED AND STRANGE THINGS WILL HAPPEN.
 

Context Stack Structure
PC
Pushed by interrupt
Y
Pushed by interrupt
X
Pushed by interrupt
D
Pushed by interrupt
CCR
Pushed by interrupt
_.tmp
Pushed by rti() Preamble or swi() Code
_.z
Pushed by rti() Preamble or swi() Code
_.xy
Pushed by rti() Preamble or swi() Code
_.frame
Pushed by rti()or swi() Preamble.  Moved by swi() Code
_.d1
Pushed by rti() Preamble or swi() Code
_.d2
Pushed by rti() or swi() Code
_.d3
Pushed by rti() or swi() Code
_.d4
Pushed by rti() or swi() Code
_.d5
Pushed by rti() or swi() Code
_.d6
Pushed by rti() or swi() Code
_.d7
Pushed by rti() or swi() Code
_.d8
Pushed by rti() or swi() Code

<-- sp

BE VERY CAREFUL IN CHANGING rti() or swi().  SOME CODE WILL FORCE SAVING SOFT REGISTERS OR MAKING ROOM VOR VARIABLES ON THE STACK.  IF THIS HAPPENS, THE CONTEXT STRUCTURE WILL BE VIOLATED AND STRANGE THINGS WILL HAPPEN.

Observations

The core of OS is the rti interrupt handler.  This handler performs a context switch to the next task every third interrupt.  This is controlled by TOCK_TICKS.

The rti task does a context switch and keeps time.  The rti rate is 4.10 msec.  There is a context switch every third rti, 12.3 msec.  The non-context switch rti is 300 usec.  The context switch rti is 400 usec.  Both these are about 20 usec longer for the max clock calculation.

With the context mechanism set to select a runable task, and togglingbit[task_num] in the context switch, the following is observed.

With all tasks defined as,
while(1) {
  port bit = 1
  serial send task number
  port bit = 0
}

The tasks cycle 1..8 continuously when all tasks remain ready to run.  task n runs for 12.2 msec and then a context switch occurs. Control is passed back to task n every 98 msec.  The rti task uses about 500 usec for each context switch, and, around 260 usec for  each non-context switch interrupt.

LCD Operations

Calling the lcd functions in bootstrap/lcd_funcs.asm and writing the rows in a loop shows lcd update to take around 3.5 msec per row or 14.0 msec for the 4x40 lcd.


Row 1 3.5 msec
Row 2 3.5 msec
Row 3 3.5 msec
Row 4 3.5 msec

So, it the context switch is adjusted to 4 ticks, each task will run for 16.4 msec and cycle thru all 8 tasks in 132 msec.  This will allow the lcd task to finish and request a task switch before it is swapped out.  ** TODO ** change swi() so it resets tocks when a context switch is requested.  This will allow the following task to have a full 4 tocks to process.  If the context switch comes via swi() it is possible the following task will have only 3 tocks + interrupt delay time.  Maybe it is possible to set the number of tocks high in the case of the lcd driver to insure a full 4 tocks and avoid any possibility of confusing the lcd.  If a context switch happend in the middle of an lcd write cycle, it is possible to violate the max time constraints on signals like E.  ** TODO ** Look at adding a 7400 (?) chip to ease the timing constraints, and, use the 68hc11 E and R/W.




LCD Pin Connections


Port
Signal
adapt-11_Pin
lcd_Pin
portb[7]
RS
14
11
portb[6]
RW
15
10
portb[5]
E1
16
9
portb[4]
E2
17
15
portb[3]
-
18
-
portb[2]
-
19
-
portb[1]
-
20
-
portb[0]
-
21
-




portc[7]
db[7]
42
1
portc[6]
db[6]
41
2
portc[5]
db[5]
40
3
portc[4]
db[4]
39
4
portc[3]
db[3]
38
5
portc[2]
db[2]
37
6
portc[1]
db[1]
36
7
portc[0]
db[0]
35
8


GPS Data

This section describes what data is extracted from the GPS NMEA data stream.  The GPS sends messages that are not discussed here.  Those messages are discarded.  The messages discussed contain information that is ignored.
$GPBOD - Bearing, Origin to Destination
$GPGGA - GPS Fix Data
$GPGLL - Geographical
$GPRMB - Generic Navigation Information
$GPRMC - GPS and Transit Specific
$GPRTE - Routes
$GPWPL - Waypoint Location

$GPBOD

    bearing (t)
    bearing (m)
    origin          need to xref this to a lat/lon
    destination     need to xref this to a lat/lon

$GPGGA

    UTC time of position  What is the difference from the other UTC, below?
    Latitude of fix
    latitude direction (N=north, S=south)  Use + for N, - for S
    Longitude of fix
    Longitude direction (W=west, E=east)   Use + for W, - for E

$GPGLL

    Present latitude (degrees and minutes)
    Present latitude direction (N=north, S=south)  Use + for N, - for S
    Present longitude (degrees and minutes)
    Present longitude direction (E=east, W=west)   Use + for W, - for E

$GPRMB

    Position valid (A=valid, V=invalid)
    Cross Track error (nautical miles)
    Direction to steer (L=left, R=right)
    Origin waypoint identifier
    Destination waypoint identifier
    Destination latitude (degrees and minutes)
    Destination latitude direction (N=north, S=south)  Use + for N, - for S
    Destination longitude (degrees and minutes)
    Destination longitude direction (E=east, W=-west)  Use + for W, - for E
    Range from present position to destination waypoint (nautical miles)
    Bearing form present position to destination wypoint (degrees, true)
    Closing velocity to destination waypoint (knots)

$GPRMC

    Time (UTC)
    Position valid (A=valid,  V=invalid)
    Latitude at UTC time (degrees and minutes)
    latitude direction (N=north, S=south)  Use + for N, - for S
    Longitude at UTC time (degrees and minutes)
    Longitude direction (E=east, W=west)   Use + for W, - for E
    Speed over ground (knots)
    Course over ground or track (degrees, true)
    Date (day, month, year)
    Magnetic variation (degrees)
    Magnetic variation direction ( E=east, W=west) Use - for W, + for E
                                                   --------------------
 NOTE DIFFERENT CONVENTION --------------------------------^

$GPRTE

    Total number of messages transmitted
    Message number
    Message mode
    Route identifier
    Waypoint identifier
    Waypoint identifier

$GPWPL

    Waypoint Latitude (degrees and minutes)
    Waypoint Latitude direction (N=north, S=south)  Use + for N, - for S
    Waypoint Longitude (degrees and minutes)
    Waypoint longitude direction (E=east, W=west)   Use + for W, - for E
    Waypoint identifier