CMPE 401 - Laboratory #2

Serial Interfacing Using the 68681 DUART


Lab Dates:

Please refer to the Lab Web Page and Lab Schedule for lab dates.

Report and Demo Due Dates:

Please refer to the Lab Web Page and Lab Schedule for all due dates.

Objectives:

Equipment, Parts, and Provided Software:

PC Host running Linux with a DB9 cable connected to the COM1 serial port
New Micros NMIX-0332-OEM microcomputer with 9-V power supply
Memory module ver. 1.1 containing MicroC/OS-II and lwip TCP/IP Stack in ROM
Breadboard signed out from lab instructor
Ribbon cable attached to memory module
60 Pin Header
Eight SF24 red LEDs
Eight 200-ohm resistors
One TTL SN74HCT374 8-bit D-type flip-flop
One 68681 DUART chip
One DB9 header and DB9 ribbon cable to connect over ttyS1
One MAX232 RS232 serial chip
One 3.6864 MHz crystal oscillator
Two 15 pF capacitors
Four 1 uF electrolytic capacitors
The cmpe401.tar file from lab1. DO NOT untar this file again or you may lose all your work from lab1
The lab2 tar file which contains all files specific to lab #2
GNU C compiler and linker

Documentation:

The course notes
The course textbook The Motorola MC68332 Microcontroller by Thomas Harman
Make and Makefiles tutorial
The 68000 Programming Reference Card
CPU32Bug Manual
MicroC/OS-II Function Summary
Lab 2 Schematic Diagram
MC68681 DUART Datasheet
Lab 2 Breadboard Layout
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:

Exercise 1:Verifying Build

  1. Download and untar the lab2 tar file into your cmpe401 directory by typing
    tar xvf lab2.tar
  2. Modify the makefile if your cmpe401 directory is not at the root of your home directory as discussed in the Makefile.
  3. 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.
  4. 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
  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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
  1. Disable PollTask() by commenting out its CreateTask() line. Enable TxTask1 and TxSendTaskA in lab2.c by uncommenting them to ensure their creation.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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 
    	
  7. 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
  1. This exercise extends exercise 3.
  2. 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).
  3. 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.
  4. 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.
  5. 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:

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