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/ttyS0 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
One 60 Pin header
Three 10 Pin headers
One 10 Pin to 10 Pin ribbon cable
One DB9 to 10 Pin ribbon cable to connect over ttyS1
One 68681 DUART chip
One MAX232 RS232 serial chip
One 3.6864 MHz crystal oscillator
One SN74HC595N Shift Register
Two 15 pF ceramic capacitors
Four 1.0 uF electrolytic capacitors
Three 0.1 uF ceramic decoupling capacitor
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
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 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.
INT8U channel = CHAN_B = 1;
INT8U 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 properly electrically connected. This initial load does not verify the DUART. The purpose of this lab, in part, is to verify the proper operation of the DUART.
Exercise 2: Software-controlled Character Echo using Polling
  1. Connect one end of the provided DB9 ribbon cable to the 10 pin header on your breadboard. Plug the DB9 side 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.
  2. 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.
  3. 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. This will echo it in the minicom 2 window.
    • Your polling routine will then output a space and then display the running total for single digit input from 0-9. After echoing the character and printing the cumulative count send a carriage return and line feed to the screen. At startup the initial count should be zero.
    • In addition, one half of the LED array will light up for a each transmitted character. The other half of the array will be lit up for each character received even the discarded ones. Do this by posting an appropriate value to the LEDQueue from inside your PollDuart routine.
    • All characters that are not between 0 and 9 should be discarded.
    • Sample output in the minicom 2 window will look like this
      	0 0
      	1 1
      	4 5
      	2 7
      	9 16
      	...
      	
  4. Test and verify your program. The characters are typed in ttyS1/minicom 2 and are echoed back into the same terminal along with the cumulative count. Load and run your program in ttyS0/minicom as before.
  5. Demonstrate your program to the lab instructor.
Exercise 3:Interrupt-driven Serial Transmission
  1. Disable PollTask() by commenting out its CreateTask() line. Enable TxASendTask and MenuTask 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 through the use of a jump table.
  3. Read through the lab2.c code to understand how the MenuTask 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 for an interrupt-driven transfer. In other words, there is both a message to transmit and the device is ready to transmit. Both conditions must be met before a transmission can occur.
  4. Write an ISR for the DUART using the template given in duart.s. The DUARTISR 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 and that the transmitter is ready to send. If the bit is set, the DUARTISR should post the TXSemA semaphore by calling OSSemPost as discussed above in the Assembly calling C section. Remember to supply the proper parameters in the proper order. The ISR should then mask out or clear 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. The copy is used because we want to change the masking bits associated with TxA only. To do that we need a copy of the current state of 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(INT8U channel, INT8U 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 on the DUART and reenable the Tx interrupt that was disabled in the previous item.
  6. If your code runs successfully you will see a prompting string in the ttyS1 window:
    	Please select from the following menu:
    	a: Trailing lights on LED array
    	b: Test loopback on channel B
    	c: Echo next character over ttyS1
    	d: Echo next character over ttyS0
    	
  7. Demonstrate your solution to the lab instructor
Exercise 4:Two-Channel Interrupt-driven Transmission and Reception
  1. This exercise extends exercise 3.
  2. Provided Code Discussion
    The prompts displayed in exercise three will prompt the user to type in a character. Once the user types in a response, that character will be processed and the specific function carried out. Option a requires no further input and the LEDS will display a trailing light pattern to indicate success. Option b will execute a test on the loopback interface on channel B of the DUART. A set character will be transmitted on TxB and subsequently verified on RxB. If the characters match, the test was successful and a status message will be displayed indicating success. If the characters does not arrive in a timely manner or the character received on RxB is not identical to the character transmitted out TxB then an error message will be displayed. Option c and d simply echo the next character to either ttyS1 (minicom 2) or ttyS0 (minicom). Junk input will be discarded and the user will be reprompted.
  3. Student Instructions
    Enable all tasks except PollTask(). Enable all four interrupt sources by uncommenting the remaining three interrupt mask lines from UserDuartInit in duart.s.
  4. Modify your DUARTISR to check for and handle all of four possible status register bits: TxRDYA, RxRDYA, TxRDYB, and RxRDYB.
  5. When a byte is received by one of the receiver channels on the DUART, the corresponding RxRDY bit will be set and an interrupt will occur. Your DUARTISR 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. Familiarize yourself with the RxBufferPost function. Your DUARTISR code will have to call the C function RxBufferPost with the proper parameters placed on the stack.
  6. 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.
  7. If you have not already done so make sure that your UserDuartTx function handles transmits for both TxA and TxB.
  8. 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 prompt, and the menu option selected will be carried out.
Exercise 5:Optional CVS demo exercise

As in the CVS exercise in Lab #1, this exercise requires no coding. You may demonstrate the checkout of a complete and current source code tree with subdirectorys for ex2, ex3, or ex4 at a minimum. Do not add or commit the temporary files or .s19 files. Further instructions for this exercise are provided here.

Report Requirements:

Consult the Report Writing 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, 2007