Jump to content
43oh

Compact async serial tx


Recommended Posts

I think you're all nuts here. This isn't 1965's mission to moon with 70k of memory, just use 2553.

Just kidding :)

 

Yeah, sorry Rob. I got caught up in oPossum's "this code is optimal (can not be done with fewer instruction words or cycles)" claim, which sounded like a challenge to me (grin).

 

Thanks for taking up the challenge. You got me to take another look at optimizing the code.

 

I really like assembly language on PIC devices so I thought it might be fun to learn assembly language on the MSP430 value line devices. So far, the von Neumann architecture on the MSP430 is proving difficult (for me), compared to the Harvard architecture on the PIC.

 

I have been programming PIC in assembly for about 20 years. Just learning the TMS430. I like it, but miss the btfss, btfsc, bsf, bcf, and btf instructions that the PIC has. CCS is a bit unstable and slow, I like MPLAB better.

Link to post
Share on other sites

So the basic compact serial tx is this...


           mov     #2, R15             ;      Serial output bitmask
           or      #0x0300, R12        ;      Stop bit(s)
           jmp     bit_low             ;      Send start bit...
bit_loop                                ;      * Add delay here if needed*
           rra     R12                 ; 7    Get bit to tx, test for zero
           jc      bit_high            ; 8->9 If high...
bit_low     bic.b   R15, &P1OUT         ; 1->4 Send zero/start bit
           jmp     bit_loop            ; 5->6 Next bit...
bit_high    bis.b   R15, &P1OUT         ; 1->4 Send one/stop bit
           jnz     bit_loop            ; 5->6 If tx data is not zero, then there are more bits to send...
           ret                         ;      Return when all bits sent

 

The versatile DCO can be used for common bit rates...

115200 @ 1.0368 MHz

57600 @ 518.4 kHz

38400 @ 345.6 kHz

19200 @ 172.8 kHz

9600 @ 86.4 kHz

3641 @ 32.768 kHz (3600 is 115200 / 32)

 

 

 

If you are programming in assembly, and are aware of MSP430 quirks, then something like this may work...

bit_loop                                ;      * Add delay here if needed*
           rra     R12                 ; 7    Get bit to tx, test for zero
           jc      bit_high            ; 8->9 If high...
bit_low     bic.b   #2, &P1OUT          ; 1->4 Send zero/start bit
           jmp     bit_loop            ; 5->6 Next bit...
bit_high    bis.b   #2, &P1OUT          ; 1->4 Send one/stop bit
           jnz     bit_loop            ; 5->6 If tx data is not zero, then there are more bits to send...
           ret                         ;      Return when all bits sent            

test        mov     #bit_low, R11
           mov     #'T' | 0x0300, R12
           call    R11
           mov     #'e' | 0x0300, R12
           call    R11
           mov     #'s' | 0x0300, R12
           call    R11
           mov     #'t' | 0x0300, R12
           call    R11
           mov     #0x030D, R12
           call    R11
           mov     #0x030A, R12
           call    R11
           jmp     $

Link to post
Share on other sites

In assembly language programs, how about storing const strings, two characters per word, inline with your code? There will be two (2) words overhead for each string plus a one time cost of fourteen (14) words for a PutStr subroutine. The subroutine pulls the string address from the stack, prints the string, then returns to the instruction immediately following the inline string. This method should save a bunch of memory when you have more than one const string to print.

 

Food for thought. Kind regards, Mike

 

        call    #PutStr                 ; print "in-line" const string
      .char    "Test",10,13,0          ; "text" +  +  + 0 terminator

PutStr                                  ; { Mike McLaren }  14 words
       mov.w   @SP, R9                 ; copy return addr to R9
bump    mov.b   @R9+, R12               ; copy string char to R12
       tst.b   R12                     ; end of string?
       jz      wrap                    ; yes, branch, else
       call    #Put232                 ; output the character and
       jmp     bump                    ; loop
wrap    bit.b   #1, R9                  ; an odd return address?
       jz      exit                    ; no, branch, else
       inc.w   R9                      ; force word boundary
exit    mov.w   R9, 0(SP)               ; update the return address
       ret                             ; ret to 1st instruct after string

Link to post
Share on other sites

Excellent!

 

I think this would work to fix up the address...

 

  inc R9        ; Must be next word
  bic #1, R9  ; Must be word aligned

 

The second line may not be needed - the PC may not have a lsb that is writeable.

 

PutStr                                  ; { Mike McLaren }  14 words
       mov.w   @SP, R9                 ; copy return addr to R9
bump    mov.b   @R9+, R12               ; copy string char to R12
       tst.b   R12                     ; end of string?
       jz      exit                    ; yes, branch, else
       call    #Put232                 ; output the character and
       jmp     bump                    ; loop
exit    inc.w   R9                      ; next word
       add     #2, SP                  ; remove return address from stack
       mov     R9, PC                  ; return to instruction after string

Link to post
Share on other sites
The second line may not be needed - the PC may not have a lsb that is writeable.

Thank you. I actually observed that once when I wrote PC with an odd value but I guess I didn't think through the implications.

 

PutStr                                  ; { Mike McLaren }  9 words
       pop     R9                      ; pop string address into R9
bump    mov.b   @R9+, R12               ; copy string char to R12
       tst.b   R12                     ; end of string?
       jz      wrap                    ; yes, branch, else
       call    #Put232                 ; output the character and
       jmp     bump                    ; loop
wrap    inc.w   R9                      ; next word
       mov.w   R9, PC                  ; goto instruction after string

Very clean, simple, and elegant! Thank you! This is exciting stuff. I really appreciate the discerning eye and tips from an assembly language person.

 

Must head off to class (EMU). I look forward to catching up with you later.

 

Cheerful regards, Mike McLaren, K8LH

 

------------------------------------

 

Just thought I'd post an example of how you might use the PutStr subroutine in an assembly language program;

 

        call    #PutStr                 ; home cursor & clear screen
      .char    0x1b,"[2J",0            ; ANSI [2J + 0 terminator
       call    #PutStr                 ; goto line 4 tab 10
      .char    0x1b,"\04;10f",0        ; ANSI \YY;XXf + 0 terminator
       call    #PutStr                 ; print title string
      .char    "K8LH Iambic Keyer Demo",0

Link to post
Share on other sites
  • 3 months later...
This is a great thread. So what changes do I need to make to get this to compile with msp430-gcc?

 

: ) unfortunately no one seemed to pick up on my hint of getting the code to work with msp430-gcc.

I gave it a try. You probably need a uniarch version of msp430-gcc to get the #include working:

 

;
;    Copyright (C) 2011  Kevin Timmerman
;
;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;    (at your option) any later version.
;
;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.
;
;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see .
;
;
;	#include "msp430g2452.h" 

       .file "serial.s"
       .arch msp430g2452
       .cpu 430
       .mpy none

       .comm bit_dur,2,2
       .comm bit_mask,2,2

       .text
       .p2align 1,0
.global serial_setup
       .type serial_setup,@function
/************************************************************
* void serial_setup(unsigned bitmask, unsigned bit_dur)
*/
serial_setup:
       mov    R15, &bit_mask      ; Save serial output bitmask
       sub    #16, R14            ; Adjust count for loop overhead
       rla    R14                 ; 4 cycle loop decrements by 8 to ensure even remainder
       mov    R14, &bit_dur       ; Save bit duration initial count
       ret                        ; Return
.Lfe1:
       .size  serial_setup,.Lfe1-serial_setup
;; End of function 

       .p2align 1,0
.global putc
       .type putc,@function
/************************************************************
* void putc(int c)
*/
putc:                                  ; Char to tx in r15
                                      ; r15, R14, r12 trashed
       mov        &bit_mask, r12      ; Serial output bitmask
       mov        &bit_dur, R14       ; Bit duration
       bis        #0x0300, r15        ; Stop bit(s)
       jmp        bit_low             ; Send start bit

bit_loop:
       mov        R14, R13            ; Get bit duration

bit_time:
       nop                            ; 4 cycle loop
       sub        #8, R13
       jc         bit_time            ;
       subc       R13, r0             ; 0 to 3 cycle delay
       nop                            ; 3
       nop                            ; 2
       nop                            ; 1
       rra        r15                 ; Get bit to tx, test for zero
       jc         bit_high            ; If high...

bit_low:
       bic.b      r12, &0x0021        ; Send zero bit P1OUT=0x0021
       jmp        bit_loop            ; Next bit...

bit_high:
       bis.b      r12, &0x0021        ; Send one bit
       jnz        bit_loop            ; If tx data is not zero, then there are more bits to send...

ret_ins:
       ret                            ; Return when all bits sent            

.Lfe2:
       .size	putc,.Lfe2-putc
;; End of function 

       .p2align 1,0
.global puts
       .type puts,@function
/************************************************************
* void puts(char *s) 
*/
puts:
push	r9
mov	r15,r9

putsloop:
mov.b	@r9+, r15
tst.b	r15
jz	puts_exit
call	#putc
jmp	putsloop

puts_exit:	
pop	r9
       ret
.Lfe3:
       .size        puts,.Lfe3-puts
;; End of function 

 

Also here for the grabbing:

https://gist.github.com/1220877

 

-rick

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...