breaking chain logo Free
Computer
Shop

Q:

I am totally failing to get TX interrupts on my MCF5272 evaluation board. You answered a question a while ago on the discussion group board and listed this pseudo code
#if SERIAL_PORT_TWO
    byte newc;
    short int fullness;
    if (*UISR(2) & UIMR_RXRDY) {
        @<process the newly arrived character 2@>
    } else if ((*UISR(2) & UIMR_TXRDY) && Taking_TX_interrupts2) {
        @<send a character or shutup 2@>
The Taking_TX_interrupts2 leads me to believe I'm not doing something to allow TX interrupts (or is this just a flag in your code?).

A:

First of all, there is nothing pseudo about that code, it compiles and runs. (or at least it did before it was ripped out of its natural habitat and converted to HTML) It is written in the C programming language using CWEB system of structured documentation. , by Donald Knuth and Silvio Levy and compiled using the Motorola Coldfire version of gcc by David Fiddes .

Here is a further clip from the same program that shows how to initialize interrupts. I wish I could show the beautifully typeset Postscript or PDF version, but I have no time to figure out how to get that through my ISP, who doesn't support FTP. So here it is partially converted to HTML and partially in source form.

Interrupt Initialization.

To make the interrupts work, we must:
@d IMR   SIM_register_word(0x0036)
@d IMR_UART1 0x1000
@d IMR_UART2 0x2000
@d IMR_TIMER2 0x0400
@<initialize and allow interrupts@>=
    @<Properly locate Interrupt Vector Table@>;
    @<initialize UART interrupts@>;
    @<initialize Timer interrupts@>;
#if SERIAL_PORT_TWO
    *IMR = (~IMR_UART1) & (~IMR_UART2) & (~IMR_TIMER2);
#else
    *IMR = (~IMR_UART1) & (~IMR_TIMER2);
#endif
    SetIntLevel(0);

We can choose which of several conditions will cause the UART to generate an interrupt. This is done by setting bits in a mask register. Right now, we want interrupts only when a new character arrives, but this mask will be changed frequently as the program runs. The same location is refered to as a mask register when it is written, and as a status register when it is read.
@d UISR(n) UART_REGS(n,0x14)
@d UIMR(n) UART_REGS_UFAKE(n,0x14)

@d UIMR_RXRDY 0x02
@d UIMR_TXRDY 0x01
@<initialize UART interrupts@>=
#if MULTIDROP
    *UIMR(1) = UIMR_RXRDY;
    Taking_TX_interrupts = 0;
#else
    *UIMR(1) = UIMR_RXRDY | UIMR_TXRDY;
    Taking_TX_interrupts = 1;
#endif

@ @<initialize second UART interrupts@>=
    *UIMR(2) = UIMR_RXRDY | UIMR_TXRDY;
    Taking_TX_interrupts2 = 1;

The |vector_table| has been set up (in ROM, possibly copied to RAM) so that interrupt vector~64 points to the Interrupt Service Routine for the serial port, |UARTinterrupt|. We also have to tell the UART hardware that this is the interrupt to generate when something happens.
@d UIVR(n) UART_REGS(n,0x30)

@<initialize UART interrupts@>+=
    *UIVR(1) = 64;          /* UART Interrupt Vector Register */

@ @<initialize second UART interrupts@>=
    *UIVR(2) = 65;

To initialize interrupts from the UART we must turn off the bit in the Interrupt Mask Register that masks out UART interrupts. When the interrupt priority mask level becomes low enough, interrupts will them be enabled.
@<initialize UART interrupts@>+=
#if MULTIDROP
    *UCR(1) = UCR_DISABLE_RX;
#else
    *UCR(1) = UCR_ENABLE_RX;
#endif
    TransmitterEnabled = 0;
    @<initialize UART Interrupt Control Register@>

@ @<initialize second UART interrupts@>=
    *UCR(2) = UCR_ENABLE_RX + UCR_ENABLE_TX;

The first UART interrupt is given level six, priority three.
@d ICR12 SIM_register_byte(0x001F)
@d ICR13 SIM_register_byte(0x0020)
@d UART_intlevel 6
@d UART_intprio 3
@d UART2_intprio 2
@<initialize UART Interrupt Control Register@>=
    *ICR12 = (UART_intlevel<<2) + UART_intprio;
    *ICR13 = (UART_intlevel<<2) + UART2_intprio;

Serial Port Initialization.

To set up UART, we must set up the UART registers themselves, and also arrange for the Interrupt Service Routine to be called when a relevant event happens. The next few sections do this. Here we make a couple of macros that set compute the address of a UART register given two parameters, the number of the UART and the offset into the register block. All the UART registers are one byte wide. Some of the registers are doubled up in a funny way. When you write to the address of the doubled register the data goes into one hardware register, but when you read the address the data comes from a different register. This sort of thing is (nearly) impossible to simulate in software, so for testing under Unix we move one of these magic one-way registers to a different address that we don't happen to be using for anything else.
@d UART_REGS(n,r) SIM_register_byte(UART1+(n-1)*(UART2-UART1)+r)
@d UART_REGS_UFAKE(n,r) SIM_register_byte(UART1+(n-1)*(UART2-UART1)+UNIX_TEST*0x40+r)

First we reset the serial port. This is done by putting a command into a Command Register.
@d UART1 0x140
@d UART2 0x180 
@d UCR(n)  UART_REGS(n,0x08)
@d UCR_RESET_TX 0x30
@d UCR_RESET_RX 0x20
@d UCR_RESET_MR 0x10
@d UCR_RESET_ERROR  0x40 
@d UCR_ENABLE_TX 0x04
@d UCR_ENABLE_RX 0x01
@d UCR_DISABLE_RX 0x02 
@d UCR_DISABLE_TX 0x08 

@<set up UART@>=
  *UCR(1) = UCR_RESET_TX;  /* reset transmitter, */
  *UCR(1) = UCR_RESET_RX;  /*... the receiver, */
  *UCR(1) = UCR_RESET_MR;  /*...and mode register */
  *UCR(1) = UCR_RESET_ERROR;

@ @<set up second UART@>=
  *UCR(2) = UCR_RESET_TX;  /* reset transmitter, */
  *UCR(2) = UCR_RESET_RX;  /*... the receiver, */
  *UCR(2) = UCR_RESET_MR;  /*...and mode register */
  *UCR(2) = UCR_RESET_ERROR;

Next set some basic operating modes.
@d UMR(n) UART_REGS(n,0x00)
@d UMR_1_PM_NONE 0x10 
@d UMR_1_PM_MULTI_DATA 0x18
@d UMR_1_BC_8 0x03
@d UMR_1_BC_7 0x02 
@d UMR_2_TXRTS 0x20 
@d UMR_2_STOP_BITS_1 0x07
@d UMR_2_STOP_BITS_2 0x0F

@<set up UART@>+=
#if MULTIDROP
  *UMR(1) = UMR_1_PM_MULTI_DATA + UMR_1_BC_7; /* mode register first write */
  *UMR(1) = UMR_2_STOP_BITS_1 + UMR_2_TXRTS;  /* mode register second write */
#else
  *UMR(1) = UMR_1_PM_NONE + UMR_1_BC_8;       /* mode register first write */
  *UMR(1) = UMR_2_STOP_BITS_1;               /* mode register second write */
#endif

@ @<set up second UART@>=
  *UCR(2) = UCR_RESET_MR;  /*...and mode register */
  *UMR(2) = UMR_1_PM_NONE + UMR_1_BC_8;       /* mode register first write */
  *UMR(2) = UMR_2_STOP_BITS_1;               /* mode register second write */


Set up the baud rate and timing.

@d UCSR(n) UART_REGS_UFAKE(n,0x04)
@d UBG1(n) UART_REGS(n,0x18)
@d UBG2(n) UART_REGS(n,0x1C)

@<set up UART@>+=
    *UCSR(1) = 0xDD;   /* Set the receive and transmit to timer mode. */
    *UBG1(1) = 0x00;   /* set 19200 bits per second */
    *UBG2(1) = 0x51;

@ @<speed up UART@>=
    *UBG1(1) = 0x00;   /* set 38400 bits per second */
    *UBG2(1) = 0x28;

@ @<set up second UART@>=
    *UCSR(2) = 0xDD;   /* Set the receive and transmit to timer mode. */
#if 1
    *UBG1(2) = 0x00;   /* set 19200 bits per second */
    *UBG2(2) = 0x51;
#else
    *UBG1(2) = 0x00;   /* set 9600 bits per second */
    *UBG2(2) = 0xA2;
#endif

Read the (bottom of the) home page (FCS main page) for information on how to contact me.