This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430FR5969 UART transmit interrupt

Other Parts Discussed in Thread: MSP430FR5969

I am having difficulty writing interrupt driven serial transmit for the FR5969 UART.

The problem begins with the documentation because the user guide is not at all clear on exactly how the interrupts work. The trick, as always, is what to do when no more data is available and what to do to start up the UART.

One possibility is that servicing the TXIFG interrupt clears that interrupt so if no more data is available you do nothing. Then when new data appears you must stuff the first byte into TXBUF before the interrupt routine takes over. That doesn't seem to apply here.

The other is that servicing the TXIFG interrupt does not clear the interrupt so that when no data is available TXIE must be cleared. Then when new data appears it must be enabled again. This is what I assumed was the case and is how I wrote my code.

All starts off well enough as an initial string of data is sent as expected. The problem is that once the program settles into its loop where it looks for serial input data and then echos it, the transmit interrupt never occurs.

Which is very odd because the transmit routine is identical for both cases. I am at a loss to explain why the initial string is sent but subsequent character at a time data is not.
 
The TI code examples are of no help here because while they show use of serial interrupts, they focus on the receive interrupt. Searching for other examples didn't turn up much. What I did find was a FreeRTOS demo that uses exactly the same method that is failing for me.

Excerpt of the code showing the transmit routines:

txint:	pushm.w	#3,r15		; save registers used (r13-r15)
	mov	#txqueue,r15	; get char from queue
	call	#dequeue
	tst	r15
	jn	txempty		; queue empty?
	mov.b	r15,&UCA0TXBUF	; no, transmit char
	jmp	txidone
txempty:
	bic.b	#1,&P1OUT
	bic	#UCTXIE,&UCA0IE
txidone: popm.w	#3,r15		; restore registers
	reti


tx:	pushm.w	#3,r14
	mov	r15,r12		; copy char to transmit
txloop:	mov	r12,r15
	mov	#txqueue,r14
	call	#enqueue
	tst	r15
	jnz	txloop		; repeat until char accepted
	jmp	txdone
txdone:	
	bis.b	#1,&P1OUT
	bis	#UCTXIE,&UCA0IE
	popm.w	#3,r14
	ret

  • 1. Is there anything specifically in MSP430FR5969 UART that makes this task difficult? I thought it was very typical of most UARTs.

    2. Could you show us the dequeue and enqueue routines?

    3. Are there anything else that might have called or jumped to them?

    4. Are there anything else accessing UCA0TX* or UCA0IV?
  • The only thing about the 5969 that is making this difficult is that it seems to work as expected and then it doesn't.

    I spent some time hacking out as much extraneous code as possible, including the receive ISR and reduced to a simple test. It prints a string and then starts a simple echo loop. The string is printed but the echo never happens. Replacing the call to the transmit routine with just stuffing the byte into TXBUF works just fine of course. This is for the GNU assembler:



    ; -*-asm-*- set emacs asm-mode #include <msp430fr5969.h> .text sys_init: mov #__stack,r1 ; temp stack mov.w #WDTPW|WDTHOLD,&WDTCTL ;disable watchdog ;; Configure all I/O ports to outputs with low out ;; mov.b #0,&P1OUT mov.b #0,&P2OUT mov.b #0,&P3OUT mov.b #0,&P4OUT mov #0,&PJOUT mov.b #-1,&P1DIR mov.b #-1,&P2DIR mov.b #-1,&P3DIR mov.b #-1,&P4DIR mov #-1,&PJDIR bic #LOCKLPM5,&PM5CTL0 ; Disable GPIO power on default ;; Unlock FRAM control register access and set 1 wait state mov #FWPW|NACCESS_1,&FRCTL0 clr.b &FRCTL0_H ;disable FRAM control mov #CSKEY,&CSCTL0 ; unlock CS registers mov.w #DCORSEL|DCOFSEL_4,&CSCTL1 ; set DCO to 16MHz ;; Set ACLK = XT1; MCLK = DCO mov.w #SELA__LFXTCLK+SELS__DCOCLK+SELM__DCOCLK,&CSCTL2 ;; SMCLK is DCO/2 or 8MHz, MCLK is DCO/1 mov #DIVA__1|DIVM__1|DIVS__2,&CSCTL3 clr.b &CSCTL0 ;; Serial port setup for console bis #UCSWRST,&UCA0CTLW0 ; reset UART mov #(UCSSEL__SMCLK|UCSWRST),&UCA0CTLW0 ; use SMCLK, 8N1 mov #13,&UCA0BRW ; set divider for 9600 mov #0x8401,&UCA0MCTLW bis.b #3,&P2SEL1 ; assign pins to TXD and RXD bic.b #3,&P2SEL0 bic #UCSWRST,&UCA0CTLW0 ; bring UART out of reset bic #UCRXIFG,&UCA0IFG mov #txqueue,r15 clr @r15 clr 2(r15) eint mov #smess,r13 mov #smlen,r12 call #pstring loop: bit #UCRXIFG,&UCA0IFG jz loop mov.b &UCA0RXBUF,r15 call #tx ;; mov.b r15,&UCA0TXBUF jmp loop ;; Generic FIFO queue routines. ;; ;; Use a simple queue structure: ;; ;; struct { ;; int head; ;; int tail; ;; q[QSIZE]; ;; }; ;; QSIZE is fixed and must be a power of two ;; ;; enqueue ;; ;; Address of queue structure is in r14 and char to insert is in ;; r15. ;; ;; Returns 0 in r15 if success or -1 if queue is full ;; .equ QSIZE,32 .func enqueue enqueue: mov @r14,r13 ; get head inc r13 ; bump to next and #QSIZE-1,r13 cmp 2(r14),r13 ; head == tail ? jeq efull ; yes, queue is full mov r13,@r14 ; update head add r13,r14 ; index into queue mov.b r15,4(r14) ; store data clr r15 ; return success ret efull: mov #-1,r15 ; return queue full flag ret .endfunc ;; ;; dequeue ;; ;; Remove an item from the queue. ;; Serial receive using UCA0. If no character is available, r14 is ;; cleared. If a character is available r14 is set to all ones and ;; the received character is placed in r15 ;; ;; Updating of the tail counter is delayed until after the ;; char is read from the queue. This prevents a possible race ;; condition. ;; ;; address of queue is in r15 ;; ;; Returned character is in r15 or -1 if nothing available ;; .func dequeue dequeue: mov 2(r15),r14 ; get tail cmp @r15,r14 ; does head == tail? jeq dempty ; yes, queue empty inc r14 ; bump tail index and #QSIZE-1,r14 mov r15,r13 ; copy queue pointer add r14,r15 ; index into queue mov.b 4(r15),r15 ; get data and #0xff,r15 ; make 8 bits mov r14,2(r13) ; update tail ret dempty: mov #-1,r15 ; indicate empty queue ret .endfunc ;; ;; Interrupt driven serial I/O routines ;; ;; The data buffers are located at the bottom of SRAM ;; .data txqueue: .skip QSIZE+4 .text ;; Interrupt service routine for USCI A0 usci_a0_isr: add.w &UCA0IV,r0 ; index into jump table reti ; Vector 0: no interrupt reti ; Vector 2: receive jmp txint ; Vector 4: transmit reti ; Vector 6: UCSTTIFG reti ; Vector 8: UCTXCPTIFG .func txint txint: pushm.w #3,r15 ; save registers used (r13-r15) mov #txqueue,r15 ; get char from queue call #dequeue tst r15 jn txempty ; queue empty? mov.b r15,&UCA0TXBUF ; no, transmit char jmp txidone txempty: bic.b #1,&P1OUT bic #UCTXIE,&UCA0IE txidone: popm.w #3,r15 ; restore registers reti .endfunc ;; Transmit character ;; ;; Repeatadly attempts to insert data in transmit queue and will not ;; give up until it works. ;; ;; Call with character to transmit in r15 ;; tx: pushm.w #3,r14 mov r15,r12 ; copy char to transmit txloop: mov r12,r15 mov #txqueue,r14 call #enqueue tst r15 jnz txloop ; repeat until char accepted jmp txdone txdone: bis.b #1,&P1OUT bis #UCTXIE,&UCA0IE popm.w #3,r14 ret ;; print the string pointed to be r13 using the count in r12 .globl pstring pstring: mov.b @r13+,r15; get next char and #0x7f,r15 call #tx ; and print it dec r12 jne pstring ; repeat ret smess: .ascii "\r\nMSP430 eForth starting\r\n" .equ smlen, .-smess ;; Interrupt vectors .section __interrupt_vector_usci_a0,"ax",@progbits .word usci_a0_isr .section .resetvec,"ax",@progbits .word sys_init .end

  • So you are accessing UCA0IV. Any such access will reset the interrupt flag.

    If txint does not actually write to TXBUF, it must set the flag again.

  • David,

     

    Clements has a good point. Please try replacing your usci_a0_isr (without changing anything else)

     

        ;; Interrupt service routine for USCI A0

    usci_a0_isr:

        bit     # UCTXIFG,& UCAxIFG

        jnz     txint

        reti

     

    There are a few other minor things. I will bring them up later.

     

    --OCY

  • I have something that seems to be working. In essence I am using the TXIE bit as a flag to signal when the transmitter is active. In the tx: subroutine I added:

    txdone:
    	bit	#UCTXIE,&UCA0IE
    	jnz	txnie
    	bis	#UCTXIE,&UCA0IE
    	bis	#UCTXIFG,&UCA0IFG
    txnie:	
    	popm.w	#3,r14
    	ret
    

  • I am not comfortable with that.

    In my very first reply, I asked: "4. Are there anything else accessing UCA0TX* or UCA0IV?"
  • You are going to have to explain your discomfort and overcome the little detail that it is working.

    The example is complete in that it contains everything UCA0 related.
  • David Schultz36 said:
    You are going to have to explain your discomfort and overcome the little detail that it is working.

    Yes. Your fix takes care of the major cause of the problem. But as I mentioned earlier, there are a few other minor things that need to be taking care of. I will try to address all of them with a proposed code modification.

    I do not have the chip nor the assembler you are using. Could you tell me:for this assembler,

    (1) mov r13,@r14 will  be treated as mov r13,0(r14) whereas mov @r14,r13 will not be treated as mov 0(r14),r13 ?

    (2) .ascii "\r\nMSP430 eForth starting\r\n" will generate a null terminated string of 27 bytes, whereas .ascii '\r\nMSP430 eForth starting\r\n' will generate a string of 26 bytes without the null ?

  • I don't see what your questions have to do with the interrupt but: The GNU assembler changes @rn into 0(rn) iff it is a destination address. If you want a null terminated ASCII string, you have to use .asciz.

**Attention** This is a public forum