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
60 Pin Header
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 ceramic capacitors
Four 1 uF electrolytic capacitors
Two 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.
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 properly electrically connected.
  4. Save this version of the s19 with a suitable name, ie. lab2a.s19.
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 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.
  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 (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.
  4. 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.
  5. 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
  1. Disable PollTask() by commenting out its CreateTask() line. Enable TxATask and TxASendTask 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 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.
  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 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.
  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 a prompting string in the ttyS1 window.
    Testing Channel A
    Please type the following character: Q
  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. 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.
  3. 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).
  4. 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.
  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 characters typed in and a status message will appear in ttyS0 indicating pass or fail of the DUART.
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 Oct 4, 2005