MicroC/OS-II source code (included in the tar file in the src directory)
Background:
Microcontrollers, such as the 68332, are usually provided with on-chip serial
and/or parallel communications interfaces. However, it is sometimes necessary
to enhance the on-chip interfaces by using external peripheral chips. In this set of laboratory exercises you will be interfacing the 68332 microcontroller with the 68681 DUART. This 8-bit 68000-family part includes two independently programmable serial interfaces, reconfigurable parallel inputs and outputs, and a
programmable hardware counter/timer.
The large number of operating modes in the DUART are controlled by means of 16 8-bit write registers and 16 8-bit read registers, which share the same 16 addresses. (Note that the registers at offset $04 and $014 should never be read. Read operations to these two register locations are used to enable special test modes that reconfigure the internal circuitry in the DUART for production test purposes). Only some of the corresponding write and read addresses access the same hardware register inside the DUART, so you cannot assume that every register that is written at one time can be read back and checked at a later time. It is up to the software to maintain a table of register contents (sometimes called shadow registers) if it needs to recall the contents that were written earlier to write-only registers.
Interfacing C Code and Assembly Language
It is necessary in this lab for C code to be able to call assembly language programs. The reverse is also true, assembly must call functions written in C. In our lab, the software development toolchain is provided by GNU. As a result, the C conventions adopted by GNU in response to Motorola determine how we call assembly from C and C from assembly.
Assembly Calling C Subroutines
An assembly language program calling a C function must first save the CPU registers before calling the function, due to the destruction of some of the register contents by the called C routine. To be safe, all CPU registers, except the stack pointer, should be saved. After saving the CPU registers, the assembly language program must move the input parameters onto the stack. Pointer values are passed as 32-bit addresses. Integers, regardless of the type(char, int, long int) are passed as 32-bit integers. The parameters are pushed onto the stack in the reverse order from the order used in the C function call. This is the normal convention for the C language. After returning from the C function, the assembly language program must adjust the stack pointer so that the parameters pushed onto the stack are "removed". The values are not actually removed, incrementing the stack pointer is sufficient. Finally, the saved CPU registers must be restored. The following example shows how an assembly language program can call the MicroC/OS-II function OSSemPost(OS_EVENT * pevent).
Ex 1 Calling OSSemPost:
movem.l A0-A6/D0-D7, -(sp) /* save CPU registers */
move.l TxSemB, -(sp) /* pass pointer to semaphore */
jsr OSSemPost /* Call C Function */
/* return parameter in register D0 */
addq.l #4, sp /* adjust stack past input parameter */
movem.l (sp)+,A0-A6/D0-D7 /* restore cpu registers */
Ex 2 Calling RxBufferPost :
movem.l A0-A6/D0-D7, -(sp) /* save CPU registers */
move.l #DATA_VALUE, -(sp) /* compiler passes everything as 32-bits */
move.l #CHANNEL, -(sp)
jsr RxBufferPost /* Call C Function */
/* return parameter in register D0*/
addq.l #8, sp /*adjust stack past input parameters */
movem.l (sp)+, A0-A6/D0-D7 /*restore CPU registers */
C Calling Assembly Subroutines
A function in C can also call an assembly language subroutine. The subroutine must know where to find the input parameters on the stack. These values are passed to the assembly language routine in a consistent order. Just before the first assembly language instruction is executed, the stack pointer will point to the 32-bit return address of the calling C code. The next stack locations will contain the input parameters. For example, the following fragment of C calls the assembly language subroutine called DuartTx.
char channel = CHAN_B = 1;
char TxByte = 0x30 = '0';
DuartTx( channel, TxByte);
Ex 1: Form for assembly language code for DuartTx when called from C
DuartTx: link A6, #0 /* create a stack frame */
movem.l A0-A6/D0-D7, -(sp) /* save CPU registers */
move.l 8(A6), D0 /* read channel into D0 */
move.l 12(A6), D1 /* read byte into D1 */
/* remaining code */
move.l (sp)+,A0-A6/D0-D7 /* restore cpu registers */
unlk A6 /* destroy stack frame */
rts
Pre-lab Work:
-
You are to arrive at the lab with the circuit pre-wired according to the schematic diagram. This will be checked at the beginning of the lab by the lab instructor and TA.
-
Review the assembly language for the 68332 that you learned in EE380. You'll make extensive use of it in this lab.
Exercise 1:Verifying Build
-
Download and untar the lab2 tar file into your cmpe401 directory by typing
tar xvf lab2.tar
-
Modify the Makefile if your cmpe401 directory is not at the root of your home directory as discussed in the Makefile.
-
Type
make to compile, assemble, and link your initial solution. As in lab one,
this program simply allows you to verify that the OS is running and that your hardware is properly electrically connected.
-
Save this version of the s19 with a suitable name, ie. lab2a.s19.
Exercise 2: Software-controlled Character Echo using Polling
-
Connect one end of the provided DB9 ribbon cable to the 10 pin header on your breadboard.
Plug the other end of this ribbon cable into the second RS232 cable coming from the back panel of the Linux Host. This cable has a label identifying it as ttyS1.
This cable is connected to the second serial port, which is accessed by
minicom 2
via /dev/ttyS1. The first serial port,
/dev/ttyS0, will continue to be used for downloading and debugging code. Feel free to open as many terminal windows as you
need to manage minicom in two windows as well as the transferring of your s19 files. You may wish to use one terminal to
compile and download your code, one terminal to run minicom and a third to run minicom 2 to
connect over /dev/ttyS1.The minicom 2 terminal will be your interface to the 68681 DUART.
-
You will modify the code in duart.s and lab2.c in this exercise. The PollTask is the only task not commented out. Leave all tasks commented out except the PollTask for this exercise. The PollTask has been setup to call the function
PollDuart every 320 ticks in the initial untarred version. Replace the 320 tick value with 6 ticks. PollDuart needs to execute
approximately 10 times per second to ensure reasonable responsiveness.
-
You are to write the PollDuart function in CPU32 assembly language. Locate the PollDuart subroutine in duart.s file. A stub
indicating where your code needs to be placed has been inserted for you.
Your PollDuart subroutine should implement the following features:
- PollDuart is to read the DUART's status register
and determine if a byte has been received by channel A's receiver. If a byte has been received and the transmitter is ready to send, it should be sent
out on channel A's transmitter (visible in minicom 2) after processing.
- You will process each alphabetic character by implementing a rudimentary enciphering
method used by Julius Caesar (100 B.C. - 44 B.C.). Each alphabetic character received must be converted by rotating the
received character by +3. For example, A becomes D, B becomes E, Z becomes C, etc. All processed characters output in the minicom 2 window should be in upper case.
-
You will support one command that displays the last plaintext character received. The character for this plaintext command is the exclamation point. If the character is an exclamation point, send a carriage return - line feed combination,
the plaintext character last received and another carriage return-line feed combination, and resume regular enciphering.
-
All other non-alphabetic characters should be sent immediately to the transmitter without converting them in any way.
-
In addition, each group of ten characters is to appear on its own line. In other words, after tranmitting ten characters,
transfer a carriage return character and a line feed character. Do not include the characters
displayed by the plaintext command in the ten characters.
-
Test and verify your program. The characters are typed in ttyS1/minicom 2 and are echoed back after converting if necessary. Load
and run your program in ttyS0/minicom as before.
-
Demonstrate your program to the lab instructor. It is recommended that you save this version of
your code as lab2b.s19 or something similar to ensure that you have a working copy of your solution should you need it later.
Exercise 3:Interrupt-driven Serial Transmission
-
Disable PollTask() by commenting out its CreateTask() line. Enable TxATask and TxASendTask in lab2.c by uncommenting them to
ensure their creation.
-
Enable TxRDYA interrupts (interrupts generated by channel A's transmitter) by uncommenting the proper interrupt mask line from
UserDuartInit() in duart.s (~ line 331). UserDuartInit is called when DuartInit is called.
-
Read through the lab2.c code to understand how the TxATask attempts to send the prompting strings
out the channel A transmitter line. TxASendTask() wakes up whenever there are bytes to send and empties the queue into the DUART's send register byte-by-byte by calling DuartTx (which calls UserDuartTx). You
will notice that before calling DuartTx(), the task blocks on the semaphore TxSemA. This is to allow an interrupt-driven transfer.
If the DUART's transmitter is empty an interrupt will occur that is supposed to signal/post this semaphore and mask out the Tx
interrupt. Once a byte is sent via DuartTx() the Tx interrupt is reenabled or unmasked.
-
Write an ISR for the DUART using the template given in duart.s. The ISR should check the DUART interrupt status register and
determine if the TxRDYA bit is set indicating that an interrupt has fired on this channel's transmitter. If the bit is set, the ISR should post the TXSemA semaphore by calling OSSemPost as discussed
above in the Assembly calling C section. Remember to supply the proper parameters. The ISR should then mask out the TxRDYA bit
using the interrupt mask register (IMR) and return. You must maintain a local copy of the IMR for reading as this is a write-only
register. Make changes to your copy and write the resulting copy to the IMR.
-
Write the UserDuartTx function in the indicated place in duart.s. This function takes two parameters using the following function
prototype
void UserDuartTx(char channel, char byte);
The transmitter channel is either CHAN_A = 0 or CHAN_B = 1, and the byte is the character to be sent. The function should move
the byte to the appropriate Tx register and enable the Tx interrupt as described in the previous item.
-
If your code runs successfully you will see a prompting string in the ttyS1 window.
Testing Channel A
Please type the following character: Q
-
Once it executes successfully, demonstrate it to the lab instructor. Rename the .s19 file to ensure that you have a copy available
if you need it later.
Exercise 4:Two-Channel Interrupt-driven Transmission and Reception
-
This exercise extends exercise 3.
-
The purpose of this exercise is to create a simple DUART tester. The string displayed in exercise three will prompt the user to
type in a certain character. Once the user types in a response, that character will be compared to the character received
on channel A's receiver. Once that is done the same test takes place on channel B with the provided character. The channel B
transmitter transmits the user character and the channel B receiver receives it. If the comparison is not equal then the
test has failed.
If either of the channels fails the test, a status string is displayed. For example, Channel A passed or Channel B failed.
This message will be displayed in ttyS0 and the prompting message of exercise three in ttyS1.
-
Enable all tasks except
PollTask(). Familiarize yourself with the RxBufferPost function. Enable all four interrupt sources by uncommenting the remaining three interrupt mask lines from
UserDuartInit in duart.s. Data received on channel A is transmitted back out on channel B's transmitter. Channel B's transmitter
is physically looped back to channel B's receiver on the DUART chip. Data received on channel B will determine what
is to be displayed in the
minicom window (ie. ttyS0).
-
Modify your ISR to check for and handle one of four possible status register bits: TxRDYA, RxRDYA, TxRDYB, and RxRDYB. When
a byte is received by one of the receiver channels, the corresponding RxRDY bit will be set and an interrupt will occur. Your
ISR should grab the byte from the proper receiver data register and post it to the correct receive queue depending on which
channel the byte was received from. Your ISR code will have to call the C function RxBufferPost with the proper parameters
placed on the stack. Multiple interrupt bits may be set, you must service all of the interrupts generated not just
the first one you encounter. Do this by reading a copy of the interrupt status register once at the beginning of your ISR.
Process all the interrrupts, one after another, corresponding to the set bits in the interrupt status register. This will
only succeed if you do not reread the interrupt status register after the initial read.
-
Upload and run the new code. Remember that the scope function in scope.s is available for you to use when debugging your ISRs.
You may also want to use the scope to probe the TX and RX pins of the DUART. If you have a successful solution, ttyS1 will display the characters typed in and a status message will appear in ttyS0 indicating pass or fail of the DUART.
Report Requirements:
- Please answer the following questions in your report.
- Briefly describe the DUART hardware and its hardware interface to the NMIX 332.
- Explain the driver software required to read and write to the DUART registers.
In other words, how is the DUART base address created.
- Several initialization steps are required to ensure that the DuartIsr is called when an
interrupt fires. What are they?
-
Your typed report is to briefly describe your design solution for the
three laboratory exercises.
-
Be sure to provide neatly formatted and well commented code for the three
exercises. Include any code that you inserted or modified, including the Makefile.
-
Briefly describe your test cases for verifying that each of your solutions
was working properly. Describe which case was tested, as well as the
anticipated and actual results for each test case.
Consult the Report Marking Guidelines
if you have any questions regarding the report format.
Marking Scheme:
Lab #2 is worth 25% of the final lab mark.
Please view the Marking Sheet
to ensure that you have completed all of the requirements of the lab.
The Marking Sheet also contains a limited test suite
in the demo section. Please make use of it.
Last modified Oct 4, 2005