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 determine how we call assembly from C and C from assembly.
Calling C from Assembly
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 */
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 assumes 32-bits */
move.l #CHANNEL, -(sp)
jsr RxBufferPost /* Call C Function */
/* return parameter in register */
addq.l #8, sp /*adjust stack past input parameters */
movem.l (sp)+, A0-A6/D0-D7 /*restore CPU registers */
Calling Assembly from C
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 connected correctly.
-
Save this version of the s19 with a suitable name, ie. lab2a.s19. Demo this with exercise 2.
Exercise 2: Software-controlled Character Echo using Polling
-
Connect one end of the provided DB9 ribbon cable to the DB9 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 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 lab2.c file contains six pre-written tasks excluding the
StartTask. Five of these
tasks are commented out. Leave these tasks commented out 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 section in duart.s file. A stub
indicating where your code needs to be placed has been inserted for you. 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 it should be sent
out on channel A's transmitter (visible in minicom 2) after processing. If a byte has been received and it is an alphabetic
character, either upper or lower case, it should be toggled to lower and upper case, respectively. Upper becomes lower and lower
becomes upper. All other characters should be sent immediately to the transmitter without converting them in any way. In addition,
each character is to appear on its own line. In other words, after tranmitting the character, transfer a carriage return
character and a line feed character. Both are standard ASCII characters.
-
Test and verify your program. The characters are typed in ttyS1 and are echoed back after converting if necessary. Load and run
your program in ttyS0 as before.
-
Demonstrate your program to the lab instructor along with the exercise 1 solution. 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 TxTask1 and TxSendTaskA 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 TxTask1 attempts to send the 3 menu option strings
out the channel A transmitter line.
TxTask1() is posting its characters to TxBufferA by using TxBufferPost(). TxSendTaskA() wakes up whenever there are bytes in
TxBufferA and empties the buffer 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 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. 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 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 three menu options appear in ttyS1. Each option appears on its own line. The options
are
1. Print Student 1's Name
2. Print Student 2's Name
3. Print Upper Case Alphabet
-
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.
-
Enable all tasks except
PollTask(). Familiarize yourself with the RxTaskA() and RxTaskB() tasks and 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 then 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 RxBufferA or RxBufferB 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.
-
Modify RxTaskA by replacing the names given with the first names of the members of your group. For those of you
working alone, use your first and last names.
-
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 3 menu options. A menu choice of 1, 2, or 3 will result in text being displayed in ttyS0 based on the menu choice chosen.
Report Requirements:
-
Briefly describe the DUART hardware and its interface to the NMIX 332. Explain how your DuartISR gets called.
-
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.
-
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 October 1, 2004