Jump to content

  • Log In with Google      Sign In   
  • Create Account

Photo

Software async serial tx/rx without timer


  • Please log in to reply
10 replies to this topic

#1 oPossum

oPossum

    Poke me with a Stick

  • Members
  • 863 posts
  • LocationMichigan, USA


Posted 16 October 2011 - 10:31 PM

Had some requests for this in IRC so here it is. This is an improved version of the serial tx previously posted and new serial rx code. Both tx and rx are blocking and interrupts should be disabled before calling.

C code to show how to use the serial_setup(), putc(), puts(), and getc() functions.
This will receive a character, increment it and echo it back. So if you type 'A', then 'B' will be echoed.
// test.c
#include "msp430g2211.h"

// Functions in serial.asm (this really should be in a header file)
void serial_setup(unsigned out_mask, unsigned in_mask, unsigned duration);
void putc(unsigned);
void puts(char *);
unsigned getc(void);

void main(void)
{
    char c;

    // Disable watchdog 
    WDTCTL = WDTPW + WDTHOLD;

    // Use 1 MHz DCO factory calibration
    DCOCTL = 0;
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;
            
    // Setup the serial port
    // Serial out: P1.1 (BIT1)
    // Serial in:  P1.2 (BIT2)
    // Bit rate:   9600 (CPU freq / bit rate)  
    serial_setup(BIT1, BIT2, 1000000 / 9600);
    
    // Send a string
    puts("\r\nRunning...\r\n");
    
    for(;<img src='http://forum.43oh.com/public/style_emoticons/<#EMO_DIR#>/icon_e_wink.gif' class='bbc_emoticon' alt=';)' /> {           // Do forever
        c = getc();     // Get a char
        ++c;            // Increment it
        putc(c);        // Echo it back
    }
}

The serial tx/rx code. Just add this as a new source file in your CCS project and it will be callable from your C code.
; serial.asm
            .cdecls C, LIST, "msp430g2231.h"

            .bss    in_bit_mask, 2      ; Serial in pin
            .bss    out_bit_mask, 2     ; Serial out pin
            .bss    bit_dur, 2          ; Bit duration in cycles
            .bss    half_dur, 2         ; Half bit duration in cycles
                                        ;
            .text                       ;
            .def    serial_setup        ; void serial_setup(unsigned out_mask, unsigned in_mask, unsigned bit_duration);
            .def    putc                ; void putc(unsigned c);
            .def    puts                ; void puts(char *s);
            .def    getc                ; unsigned getc(void);
                                        ;
                                        ;
serial_setup                            ; - Setup serial I/O bitmasks and bit duration (32 minimum)
            mov     R12, &out_bit_mask  ; Save serial output bitmask
            mov     R13, &in_bit_mask   ; Save serial input bitmask
            bis.b   R12, &P1DIR         ; Setup output pin
            bis.b   R12, &P1OUT         ;
            bic.b   R13, &P1DIR         ; Setup input pin
            or      R13, R12            ;
            bic.b   R12, &P1SEL         ; Setup peripheral select
            mov     R14, R12            ;
            sub     #16, R14            ; Adjust count for loop overhead
            rla     R14                 ; Multiply by 2 because NOP is two bytes
            mov     R14, &bit_dur       ; Save bit duration
            sub     #32, R12            ; Adjust count for loop overhead
            mov     R12, &half_dur      ; Save half bit duration
            ret                         ; Return
                                        ;
                                        ; - Send a single char
putc                                    ; Char to tx in R12
                                        ; R12, R13, R14, R15 trashed
            mov     &out_bit_mask, R15  ; Serial output bitmask
            mov     &bit_dur, R14       ; Bit duration
            or      #0x0300, R12        ; Stop bit(s)
            jmp     bit_low             ; Send start bit...
                                        ;
tx_bit      mov     R14, R13            ; Get bit duration
tx_delay    nop                         ; 4 cycle loop
            sub     #8, R13             ;
            jc      tx_delay            ;
            subc    R13, PC             ; 0 to 3 cycle delay
            nop                         ; 3
            nop                         ; 2
            nop                         ; 1
                                        ;
            rra     R12                 ; Get bit to tx, test for zero
            jc      bit_high            ; If high...
bit_low     bic.b   R15, &P1OUT         ; Send zero bit
            jmp     tx_bit              ; Next bit...
bit_high    bis.b   R15, &P1OUT         ; Send one bit
            jnz     tx_bit              ; If tx data is not zero, then there are more bits to send...
                                        ;
            ret                         ; Return when all bits sent         
                                        ;
                                        ;
                                        ; - Send a NULL terminated string
puts                                    ; Tx string using putc
            push    R11                 ;
            mov     R12, R11            ; String pointer in R12, copy to R11
putsloop                                ;
            mov.b   @R11+, R12          ; Get a byte, inc pointer
            tst.b   R12                 ; Test if end of string
            jz      putsx               ; Yes, exit...
            call    #putc               ; Call putc
            jmp     putsloop            ;
putsx       pop     R11                 ;
            ret                         ;
                                        ;
getc                                    ; - Get a char
            mov     &bit_dur, R14       ; Bit duration
            mov     &in_bit_mask, R13   ; Input bitmask
            mov     #0x01FF, R12        ; 9 bits - 8 data + stop
                                        ;
rx_start                                ; Wait for start bit
            mov.b   &P1IN, R15          ; Get serial input          
            and     R13, R15            ; Mask and test bit
            jc      rx_start            ; Wait for low...
                                        ;
            mov     &half_dur, R13      ; Wait for 1/2 bit time
                                        ;
rx_delay    nop                         ; Bit delay
            sub     #8, R13             ;
            jc      rx_delay            ;
            subc    R13, PC             ; 0 to 3 cycle delay
            nop                         ; 3
            nop                         ; 2
            nop                         ; 1
                                        ;
            mov.b   &P1IN, R15          ; Get serial input          
            and     &in_bit_mask, R15   ;
            rrc     R12                 ; Shift in a bit
                                        ;
            mov     R14, R13            ; Setup bit timer
            jc      rx_delay            ; Next bit...
                                        ;
            rla     R12                 ; Move stop bit to carry
            swpb    R12                 ; Move rx byte to lower byte, start bit in msb
            ret                         ; Return with rx char and start bit in R12, stop bit in carry
                                        ;                                       
            .end                        ;

  • bluehash, DrMag, timotet and 15 others like this
Think in assembly, write in C. Sent from a bunker in an undisclosed location deep beneath a mountain.

#2 Rickta59

Rickta59

    Level 3

  • Global Moderators
  • 719 posts
  • LocationEastern NC


Posted 23 October 2011 - 03:48 PM

Here is an msp430-gcc port of serial.S. Only some minor changes required:

renamed from serial.asm to serial.S
changed header inclusion, .cdecls to #include
added ':' to all labels
changed .bss directives to .lcomm
changed .def directives to .global
fixed argument registers CCS using R12-R15 for arg1-arg4, GCC uses R15-R12 for arg1-arg4
reworked temp registers because of argument register reorder
changed OR asm statements to BIS statements

; serial.S - gcc port of serial.asm
#include <msp430.h>

;-------------------------------------------------------------------------------
; --- define PC,SP,SR gcc only knows about registers ---

#define PC r0                   
#define SP r1
#define SR r2

#define ARG1 R15
#define ARG2 R14
#define ARG3 R13
#define ARG4 R12

            .lcomm  in_bit_mask, 2      ; Serial in pin
            .lcomm  out_bit_mask, 2     ; Serial out pin
            .lcomm  bit_dur, 2          ; Bit duration in cycles
            .lcomm  half_dur, 2         ; Half bit duration in cycles
                                        ;
            .text                       ;
            .global serial_setup        ; void serial_setup(unsigned out_mask, unsigned in_mask, unsigned bit_duration);
            .global putc                ; void putc(unsigned c);
            .global puts                ; void puts(char *s);
            .global getc                ; unsigned getc(void);
                                        ;
            .p2align 1,0                ; align on a word boundary
serial_setup:				; - Setup serial I/O bitmasks and bit duration (32 minimum)
            mov     ARG1, &out_bit_mask ; Save serial output bitmask
            mov     ARG2, &in_bit_mask  ; Save serial input bitmask
            bis     ARG1, &P1DIR        ; Setup output pin
            bis     ARG1, &P1OUT        ;
            bic     ARG2, &P1DIR        ; Setup input pin
            bis     ARG2, ARG1          ;
            bic     ARG1, &P1SEL        ; Setup peripheral select
            mov     ARG3, ARG1          ;
            sub     #16, ARG3           ; Adjust count for loop overhead
            rla     ARG3                ; Multiply by 2 because NOP is two bytes
            mov     ARG3, &bit_dur      ; Save bit duration
            sub     #32, ARG1           ; Adjust count for loop overhead
            mov     ARG1, &half_dur     ; Save half bit duration
            ret                         ; Return
                                        ;
                                        ; - Send a single char
putc:					; Char to tx in R15
                                        ; R12, R13, R14, R15 trashed
            mov     &out_bit_mask, R12  ; Serial output bitmask
            mov     &bit_dur, R14       ; Bit duration
            bis     #0x0300, ARG1       ; Add Stop bit(s) to tx char
            jmp     bit_low             ; Send start bit...
                                        ;
tx_bit:     mov     R14, R13            ; Get bit duration
tx_delay:   nop                         ; 4 cycle loop
            sub     #8, R13             ;
            jc      tx_delay            ;
            subc    R13, PC             ; 0 to 3 cycle delay
            nop                         ; 3
            nop                         ; 2
            nop                         ; 1
                                        ;
            rra     ARG1                ; Get bit to tx, test for zero
            jc      bit_high            ; If high...
bit_low:    bic.b   R12, &P1OUT         ; Send zero bit
            jmp     tx_bit              ; Next bit...
bit_high:   bis.b   R12, &P1OUT         ; Send one bit
            jnz     tx_bit              ; If tx data is not zero, then there are more bits to send...
                                        ;
            ret                         ; Return when all bits sent         
                                        ;
                                        ;
                                        ; - Send a NULL terminated string
puts:                                   ; Tx string using putc
            push    R11                 ;
            mov     ARG1, R11           ; String pointer in R15, copy to R11
putsloop:                               ;
            mov.b   @R11+, ARG1         ; Get a byte, inc pointer
            tst.b   ARG1                ; Test if end of string
            jz      putsx               ; Yes, exit...
            call    #putc               ; Call putc
            jmp     putsloop            ;
putsx:      pop     R11                 ;
            ret                         ;
                                        ;
getc:                                   ; - Get a char
            mov     &bit_dur, R14       ; Bit duration
            mov     &in_bit_mask, R13   ; Input bitmask
            mov     #0x01FF, ARG1       ; 9 bits - 8 data + stop
                                        ;
rx_start:                               ; Wait for start bit
            mov     &P1IN, R12          ; Get serial input          
            and     R13, R12            ; Mask and test bit
            jc      rx_start            ; Wait for low...
                                        ;
            mov     &half_dur, R13      ; Wait for 1/2 bit time
                                        ;
rx_delay:   nop                         ; Bit delay
            sub     #8, R13             ;
            jc      rx_delay            ;
            subc    R13, PC             ; 0 to 3 cycle delay
            nop                         ; 3
            nop                         ; 2
            nop                         ; 1
                                        ;
            mov     &P1IN, R12          ; Get serial input          
            and     &in_bit_mask, R12   ;
            rrc     ARG1                ; Shift in a bit
                                        ;
            mov     R14, R13            ; Setup bit timer
            jc      rx_delay            ; Next bit...
                                        ;
            rla     ARG1                ; Move stop bit to carry
            swpb    ARG1                ; Move rx byte to lower byte, start bit in msb
            ret                         ; Return with rx char and start bit in R15, stop bit in carry

-rick
  • oPossum, gordon, zborgerd and 3 others like this

#3 Theshadowwalker91

Theshadowwalker91

    Advanced Member

  • Members
  • PipPipPip
  • 43 posts

Posted 04 December 2011 - 06:43 AM

how would i go about reading serial data sent from an arduino. i the arduino is sending this

Serial1.print ('<'); //start
Serial1.print ('5'); // address 1-5
Serial1.print ("001"); // Foreground is Blue
Serial1.print ("010"); // Background is Green
Serial1.print ("100"); // Marker is Red
Serial1.print ('>'); // end

#4 oPossum

oPossum

    Poke me with a Stick

  • Members
  • 863 posts
  • LocationMichigan, USA


Posted 04 December 2011 - 07:41 AM


unsigned state, c, n, addr, fgr, fgg, fgb, bgr, bgg, bgb, mkr, mkg, mkb;



for(state = 0; <img src='http://forum.43oh.com/public/style_emoticons/<#EMO_DIR#>/icon_e_wink.gif' class='bbc_emoticon' alt=';)' /> {

    c = getc();

    if(c == '<') {

        state = 1;

    } else if(c == '>') {

        if(state == 11) {

            // End of packet - do something

        }

        state = 0;

    } else if(c >= '0' && c <= '9') {

        n = c - '0';

        if(state > 1 && n > 1) state = 0;

        switch(state) {

            case 1: ++state; addr = n; break;

            case 2: ++state; fgr = n; break;

            case 3: ++state; fgg = n; break;

            case 4: ++state; fgb = n; break;

            case 5: ++state; bgr = n; break;

            case 6: ++state; bgg = n; break;

            case 7: ++state; bgb = n; break;

            case 8: ++state; mkr = n; break;

            case 9: ++state; mkg = n; break;

            case 10: ++state; mkb = n; break;

            default: break;

        }

    } else {

        state = 0;

    }

}


  • bluehash and Theshadowwalker91 like this
Think in assembly, write in C. Sent from a bunker in an undisclosed location deep beneath a mountain.

#5 Theshadowwalker91

Theshadowwalker91

    Advanced Member

  • Members
  • PipPipPip
  • 43 posts

Posted 11 December 2011 - 08:16 PM

also i ment how would i read the data and based on it turn on an led

#6 Theshadowwalker91

Theshadowwalker91

    Advanced Member

  • Members
  • PipPipPip
  • 43 posts

Posted 18 December 2011 - 04:54 PM

can anyone here help me. i am not sure how to use this code to turn on an led based on the serial data that is recived.

#7 rockets4kids

rockets4kids

    Level 3

  • Members
  • 410 posts

Posted 29 April 2012 - 12:24 AM

[ EDIT: be sure to see updated post below ]

Many thanks to Rick and oPossum for helping me get this up and running with gcc 4.6.3. In the process we found a few problems in the original code where word access is made to port registers rather than byte mode.

Here is what I have:

sw_serial.S
;------------------------------------------------------------------------
; File: sw_serial.S - msp430-gcc software only async serial routines
;
;    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 <http://www.gnu.org/licenses/>.
;
;
; Desc: blocking versions of putchar(), getchar(), and puts() that
;       use cycle counting for accurate sampling and sending. Features
;       very small code size, allows use of any pin, and doesn't interfere
;       with the use of timers.
;
; Change Log:
;    2012-Jan-04 rick@kimballsoftware.com
;                Changes for gcc, renamed function names to
;                allow gcc printf() to work, conditional defines to allow
;                for smaller code, disabled puts() by default so not to
;                conflict with libc version. renamed jump labels to
;                reduce symbol table pollution.
;
;    2011-Oct-23 rick@kimballsoftware.com
;                Initial port to msp430-gcc
;
;    2012-Apr-28 rockets4kids@gmail.com
;                rolled in include file
;                fixed I/O ops to byte (.<img src='http://forum.43oh.com/public/style_emoticons/<#EMO_DIR#>/cool.png' class='bbc_emoticon' alt='B)' /> mode
;
; See Also:
;   Detailed information about original code here:
;   http://www.43oh.com/forum/viewtopic.php?f=10&t=1727
;
;------------------------------------------------------------------------
; TODO: Add optional sleep mode in getchar() which will disable MCU until
;       incoming pin edge detect triggers interrupt.
; TODO: Allow for mulitple serial ports
;------------------------------------------------------------------------

#include <msp430.h>

//------------------------------------------------------------------------------
// define PC,SP,SR as gcc only uses the register names
//
#define PC r0                   
#define SP r1
#define SR r2

//------------------------------------------------------------------------------
// define argument registers
//  C function arguments are passed in R15-R12
//
// Note: these registers can be used without preservation
//
#define ARG1 R15
#define ARG2 R14
#define ARG3 R13
#define ARG4 R12

        
#define ENABLE_GETCHAR              /* enable getchar() */
#define ENABLE_PUTCHAR              /* enable putchar() */
#undef  ENABLE_PUTS                 /* disable puts() as it isn't ISO conforming */
#define ENABLE_AUTO_GIE             /* call __dint() .. __eint() on getchar() putchar() */

        .file "sw_serial.S"

;------------------------------------------------------------------------
; Unitialized SRAM local variables
;------------------------------------------------------------------------
        .lcomm  rx_bit_mask, 2      ; Serial in pin mask
        .lcomm  tx_bit_mask, 2      ; Serial out pin mask
        .lcomm  bit_dur, 2          ; Bit duration in cycles
        .lcomm  half_dur, 2         ; Half bit duration in cycles

;------------------------------------------------------------------------
; Exposed Function Symbols
;------------------------------------------------------------------------
        .text                       ; use the "text" section for all code below
        .global init_serial         ; void init_serial(unsigned out_mask, unsigned in_mask, unsigned bit_duration);
        .global init_serial1        ; void init_serial(_serial_ios *ios);
#ifdef ENABLE_GETCHAR
        .global getchar             ; int getchar(void);
#endif
#ifdef ENABLE_PUTCHAR
        .global putchar             ; int putchar(uint8_t c);
#endif
#ifdef ENABLE_PUTS
        .global puts                ; int puts(const char *s); NOTE: no appended newline
#endif

;------------------------------------------------------------------------
; Serial Function Implementations
;------------------------------------------------------------------------

        .p2align 1,0                ; align on a word boundary
;--------------------------------------------------------------------------------
; void init_serial(int txPinMask, int rxPinMask, int bitDuration)
;--------------------------------------------------------------------------------
        .type init_serial,@function
init_serial:                        ; Setup serial I/O bitmasks and bit duration (minimum of 32)
        mov     ARG1, &tx_bit_mask  ; Save serial output bitmask
        mov     ARG2, &rx_bit_mask  ; Save serial input bitmask
        bis.b   ARG1, &P1DIR        ; set output pin
        bis.b   ARG1, &P1OUT
        bic.b   ARG2, &P1DIR        ; clear input pin
        bis     ARG2, ARG1
#ifdef __MSP430FR5739__
        bic.b   ARG1, &P1SEL0       ; force output pin to digial I/O no pullups
        bic.b   ARG1, &P1SEL1       ; force output pin to digial I/O no pullups
#else
        bic.b   ARG1, &P1SEL        ; force output pin to digial I/O no pullups
#endif
        mov     ARG3, ARG1          ; Compute bit duration
        sub     #16, ARG3           ; Adjust count for loop overhead
        rla     ARG3                ; Multiply by 2 because NOP is two bytes
        mov     ARG3, &bit_dur      ; Save bit duration
        sub     #32, ARG1           ; Adjust count for loop overhead
        mov     ARG1, &half_dur     ; Save half bit duration
        ret                         ; ready to rx/tx
.Lfe1:
        .size init_serial,.Lfe1-init_serial


#ifdef ENABLE_GETCHAR
;--------------------------------------------------------------------------------
; int getchar(void) - read one character (blocking)
;--------------------------------------------------------------------------------
        .p2align 1,0                ; align on a word boundary
        .type getchar,@function
getchar:
        bic     #GIE, SR            ; disable global interrupts
        mov     &bit_dur, R14       ; Bit duration
        mov     &rx_bit_mask, R13   ; Input bitmask
        mov     #0x01FF, ARG1       ; 9 bits - 8 data + stop

.rx_start:                          ; Wait for start bit
        mov.b   &P1IN, R12          ; Get serial input
        and     R13, R12            ; Mask and test bit
        jc      .rx_start           ; Wait for low...
        mov     &half_dur, R13      ; Wait for 1/2 bit time

.rx_delay:
        nop                         ; Bit delay
        sub     #8, R13
        jc      .rx_delay
        subc    R13, PC             ; 0 to 3 cycle delay
        nop                         ; 3
        nop                         ; 2
        nop                         ; 1

        mov.b   &P1IN, R12          ; Get serial input
        and     &rx_bit_mask, R12
        rrc     ARG1                ; Shift in a bit

        mov     R14, R13            ; Setup bit timer
        jc      .rx_delay           ; Next bit...

        rla     ARG1                ; Move stop bit to carry
        swpb    ARG1                ; Move rx byte to lower byte, start bit in msb
        bis     #GIE, SR            ; enable global interrupts
        ret                         ; Return with rx char and start bit in R15, stop bit in carry
.Lfe3:
        .size getchar,.Lfe3-getchar
#endif


#ifdef ENABLE_PUTCHAR
;--------------------------------------------------------------------------------
; int putchar(uint8_t c) - writes the character c to serial out
;--------------------------------------------------------------------------------
        .p2align 1,0                ; align on a word boundary
        .type putchar,@function
putchar:                                ; Char to tx in R15, R12, R13, R14, R15 trashed
        bic     #GIE, SR            ; disable global interrupts
        mov     &tx_bit_mask, R12       ; Serial output bitmask
        mov     &bit_dur, R14       ; Bit duration
        bis     #0x0300, ARG1       ; Add Stop bit(s) to tx char
        jmp     .bit_low            ; Send start bit...

.tx_bit:
        mov     R14, R13            ; Get bit duration
.tx_delay:
        nop                         ; 4 cycle loop
        sub     #8, R13
        jc      .tx_delay
        subc    R13, PC             ; 0 to 3 cycle delay
        nop                         ; 3
        nop                         ; 2
        nop                         ; 1
        rra     ARG1                ; Get bit to tx, test for zero
        jc      .bit_high           ; If high...
.bit_low:
        bic.b   R12, &P1OUT        ; Send zero bit
        jmp     .tx_bit             ; Next bit...
.bit_high:
        bis.b   R12, &P1OUT        ; Send one bit
        jnz     .tx_bit             ; If tx data is not zero, then there are more bits to send...

        bis     #GIE, SR            ; enable global interrupts
        ret                         ; Return when all bits sent
.Lfe2:
        .size putchar,.Lfe2-putchar
#endif


#ifdef ENABLE_PUTS
;--------------------------------------------------------------------------------
; int puts(const char *s) - writes the string s to serial out.
;--------------------------------------------------------------------------------
        .p2align 1,0                ; align on a word boundary
        .type puts,@function
puts:
        push    R11                 ; TX string using putchar
        mov     ARG1, R11           ; String pointer in R15, copy to R11 (temp reg)
.putsloop:
        mov.b   @R11+, ARG1         ; Get a byte, inc pointer
        tst.b   ARG1                ; Test if end of string
        jz      .putsx              ; Yes, exit...
        call    #putchar            ; Call putchar
        jmp     .putsloop
.putsx:
        pop     R11                 ; restore original R11
        ret                         ;
.Lfe4:
        .size puts,.Lfe4-puts
#endif

sw_serial.h
void init_serial(unsigned txPinMask, unsigned rxPinMask, unsigned bitDuration);
int getchar(void);
int putchar(int c);
int puts(const char *s);

main.c
#include <msp430.h>
#include <stdlib.h>
#include "sw_serial.h"

void main(void)
{
    int c;

    // Disable watchdog 
    WDTCTL = WDTPW + WDTHOLD;

    // Use 1 MHz DCO factory calibration
    DCOCTL = 0;
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;
            
    // Setup the serial port
    // Serial out: P1.1 (BIT1)
    // Serial in:  P1.2 (BIT2)
    // Bit rate:   9600 (CPU freq / bit rate)  
    init_serial (BIT1, BIT2, 1000000 / 9600);
    
    // Send a string
    puts ("\r\nRunning...\r\n");
    
    for (;<img src='http://forum.43oh.com/public/style_emoticons/<#EMO_DIR#>/icon_e_wink.gif' class='bbc_emoticon' alt=';)' /> {           // Do forever
        c = getchar();     // Get a char
        putchar (c);        // Echo it back
    }
}

Makefile
lazy.elf: sw_serial.S main.c
	msp430-gcc -Os sw_serial.S main.c -o lazy.elf -mmcu=msp430g2211

install: lazy.elf
	mspdebug rf2500 "prog lazy.elf"

clean:
	rm -f *~ *.elf

  • bluehash and gordon like this

#8 rockets4kids

rockets4kids

    Level 3

  • Members
  • 410 posts

Posted 29 April 2012 - 06:52 PM

I did not realize it at the time, but the code I was working from hed edits beyond oPossum's original code. I have gone back and re-patched for gcc from oPossum's code -- hopefully not introducing any new bugs this time!

I did make one change, to re-name all the the public functions to start with "serial_" for the sake of clarity.


serial.S
#include <msp430.h>

#define PC r0                   
#define SP r1
#define SR r2
#define ARG1 R15
#define ARG2 R14
#define ARG3 R13
#define ARG4 R12

; serial.asm

            .lcomm  in_bit_mask, 2      ; Serial in pin
            .lcomm  out_bit_mask, 2     ; Serial out pin
            .lcomm  bit_dur, 2          ; Bit duration in cycles
            .lcomm  half_dur, 2         ; Half bit duration in cycles
                                        ;
            .text                       ;
            .global serial_setup        ; void serial_setup(unsigned out_mask, unsigned in_mask, unsigned bit_duration);
            .global serial_putc         ; void serial_putc(unsigned c);
            .global serial_puts         ; void serial_puts(char *s);
            .global serial_getc         ; unsigned serial_getc(void);
                                        ;
            .p2align 1,0                ; align on a word boundary (GCC)
                                        ;
serial_setup:                           ; - Setup serial I/O bitmasks and bit duration (32 minimum)
            mov     ARG1, &out_bit_mask ; Save serial output bitmask
            mov     ARG2, &in_bit_mask  ; Save serial input bitmask
            bis.b   ARG1, &P1DIR        ; Setup output pin
            bis.b   ARG1, &P1OUT        ;
            bic.b   ARG2, &P1DIR        ; Setup input pin
            bis     ARG2, ARG1          ;
            bic.b   ARG1, &P1SEL        ; Setup peripheral select
            mov     ARG3, ARG1          ;
            sub     #16, ARG3           ; Adjust count for loop overhead
            rla     ARG3                ; Multiply by 2 because NOP is two bytes
            mov     ARG3, &bit_dur      ; Save bit duration
            sub     #32, ARG1           ; Adjust count for loop overhead
            mov     ARG1, &half_dur     ; Save half bit duration
            ret                         ; Return
                                        ;
                                        ; - Send a single char
serial_putc:                            ; Char to tx in ARG1
                                        ; ARG1, ARG2, ARG3, ARG4 trashed
            mov     &out_bit_mask, ARG4 ; Serial output bitmask
            mov     &bit_dur, ARG3      ; Bit duration
            bis     #0x0300, ARG1       ; Stop bit(s)
            jmp     bit_low             ; Send start bit...
                                        ;
tx_bit:     mov     ARG3, ARG2          ; Get bit duration
tx_delay:   nop                         ; 4 cycle loop
            sub     #8, ARG2            ;
            jc      tx_delay            ;
            subc    ARG2, PC            ; 0 to 3 cycle delay
            nop                         ; 3
            nop                         ; 2
            nop                         ; 1
                                        ;
            rra     ARG1                ; Get bit to tx, test for zero
            jc      bit_high            ; If high...
bit_low:    bic.b   ARG4, &P1OUT        ; Send zero bit
            jmp     tx_bit              ; Next bit...
bit_high:   bis.b   ARG4, &P1OUT        ; Send one bit
            jnz     tx_bit              ; If tx data is not zero, then there are more bits to send...
                                        ;
            ret                         ; Return when all bits sent         
                                        ;
                                        ;
                                        ; - Send a NULL terminated string
serial_puts:                            ; Tx string using serial_putc
            push    R11                 ;
            mov     ARG1, R11           ; String pointer in ARG1, copy to R11
putsloop:                               ;
            mov.b   @R11+, ARG1         ; Get a byte, inc pointer
            tst.b   ARG1                ; Test if end of string
            jz      putsx               ; Yes, exit...
            call    #serial_putc        ; Call serial_putc
            jmp     putsloop            ;
putsx:      pop     R11                 ;
            ret                         ;
                                        ;
serial_getc:                            ; - Get a char
            mov     &bit_dur, ARG3      ; Bit duration
            mov     &in_bit_mask, ARG2  ; Input bitmask
            mov     #0x01FF, ARG1       ; 9 bits - 8 data + stop
                                        ;
rx_start:                               ; Wait for start bit
            mov.b   &P1IN, ARG4         ; Get serial input          
            and     ARG2, ARG4          ; Mask and test bit
            jc      rx_start            ; Wait for low...
                                        ;
            mov     &half_dur, ARG2     ; Wait for 1/2 bit time
                                        ;
rx_delay:   nop                         ; Bit delay
            sub     #8, ARG2            ;
            jc      rx_delay            ;
            subc    ARG2, PC            ; 0 to 3 cycle delay
            nop                         ; 3
            nop                         ; 2
            nop                         ; 1
                                        ;
            mov.b   &P1IN, ARG4         ; Get serial input          
            and     &in_bit_mask, ARG4  ;
            rrc     ARG1                ; Shift in a bit
                                        ;
            mov     ARG3, ARG2          ; Setup bit timer
            jc      rx_delay            ; Next bit...
                                        ;
            rla     ARG1                ; Move stop bit to carry
            swpb    ARG1                ; Move rx byte to lower byte, start bit in msb
            ret                         ; Return with rx char and start bit in ARG1, stop bit in carry
                                        ;                                       


serial.h
void serial_setup (unsigned out_mask, unsigned in_mask, unsigned duration);
void serial_putc (unsigned);
void serial_puts (char *);
unsigned serial_getc (void);


main.c
#include <msp430.h>
#include "serial.h"


void main(void)
{
    char c;

    // Disable watchdog 
    WDTCTL = WDTPW + WDTHOLD;

    // Use 1 MHz DCO factory calibration
    DCOCTL = 0;
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;
            
    // Setup the serial port
    // Serial out: P1.1 (BIT1)
    // Serial in:  P1.2 (BIT2)
    // Bit rate:   9600 (CPU freq / bit rate)  
    serial_setup (BIT1, BIT2, 1000000 / 9600);
    
    // Send a string
    serial_puts ("\r\nRunning...\r\n");
    
    for (;<img src='http://forum.43oh.com/public/style_emoticons/<#EMO_DIR#>/icon_e_wink.gif' class='bbc_emoticon' alt=';)' /> {           // Do forever
        c = serial_getc ();     // Get a char
        serial_putc (c);        // Echo it back
    }
}


Makefile
main.elf: serial.S main.c
	msp430-gcc -Os serial.S main.c -o main.elf -mmcu=msp430g2211

install: main.elf
	mspdebug rf2500 "prog main.elf"

clean:
	rm -f *~ *.elf

  • oPossum and woodgrain like this

#9 DecebaL

DecebaL

    Noob Class

  • Members
  • 2 posts

Posted 31 January 2013 - 09:24 PM

Hi!
Please help someone.
How to use this code with even parity bits?


#10 roadrunner84

roadrunner84

    Level 4

  • Members
  • 1,031 posts
  • LocationEindhoven, the Netherlands

Posted 01 February 2013 - 10:33 AM

I'm not a real ASM guy, but I'm quite familiar with it, so I'll give it a try:

This is my modified serial_putc:

                                        ; - Send a single char
serial_putc:                            ; Char to tx in ARG1
                                        ; ARG1, ARG2, ARG3, ARG4 trashed
            push    R10                 ;>Have another register to play with
            push    R11                 ;>Have another register to play with
            mov     #0, R11             ;>Clear out R11 (odd parity with #1)
            mov     #0x0006, R10        ;>Set R10 to "parity and stop to go"
            mov     &out_bit_mask, ARG4 ; Serial output bitmask
            mov     &bit_dur, ARG3      ; Bit duration
            sub     #8, ARG3            ;>We need another 8 cycles for parity magic
            bis     #0x0600, ARG1       ;>Stop bit(s)
            jmp     bit_low             ; Send start bit...
                                        ;
tx_bit:     mov     ARG3, ARG2          ; Get bit duration
tx_delay:   nop                         ; 4 cycle loop
            sub     #8, ARG2            ;
            jc      tx_delay            ;
            subc    ARG2, PC            ; 0 to 3 cycle delay
            nop                         ; 3
            nop                         ; 2
            nop                         ; 1
            xor     ARG1, R11           ;>calculate parity (1 cycle)
            cmp     R10, ARG1           ;>next bit to send is parity? (1 cycle)
            jne     bit_out_w           ;>no? output bit (2 cycles)
            bit     #1, R11             ;>parity bit set? (1 cycle)
            addc    #0, ARG1            ;>add parity to data byte (1 cycle)
            jmp     bit_out             ;>skip the compensating delay (2 cycles)
bit_out_w:  nop                         ;>
            nop                         ;>
            nop                         ;>
            nop                         ;>
bit_out:    rra     ARG1                ; Get bit to tx, test for zero
            jc      bit_high            ; If high...
bit_low:    bic.b   ARG4, &P1OUT        ; Send zero bit
            jmp     tx_bit              ; Next bit...
bit_high:   bis.b   ARG4, &P1OUT        ; Send one bit
            jnz     tx_bit              ; If tx data is not zero, then there are more bits to send...
                                        ;
            pop     R11                 ;>Restore R11
            pop     R10                 ;>Restore R10
            ret                         ; Return when all bits sent         

You can use it for either parity (even or odd).

 

I'll be writing the receive part now, do you want to actually check the parity, or just assume everything is ok?

This is the receive routine, I did not verify the parity (since you cannot return any error anyway). Instead, the parity bit is stored in the higher byte of the result. Would you change

void main(void)
{
    char c;
...

to

void main(void)
{
    short c;
...

you could check bit 0x0100 for the parity information and process it in C code if you wish. The parity bit is passed as is, so it's not status information on the validity of the data.

serial_getc:                            ; - Get a char
            mov     &bit_dur, ARG3      ; Bit duration
            mov     &in_bit_mask, ARG2  ; Input bitmask
            mov     #0x03FF, ARG1       ;>10 bits - 8 data + parity + stop
                                        ;
rx_start:                               ; Wait for start bit
            mov.b   &P1IN, ARG4         ; Get serial input          
            and     ARG2, ARG4          ; Mask and test bit
            jc      rx_start            ; Wait for low...
                                        ;
            mov     &half_dur, ARG2     ; Wait for 1/2 bit time
                                        ;
rx_delay:   nop                         ; Bit delay
            sub     #8, ARG2            ;
            jc      rx_delay            ;
            subc    ARG2, PC            ; 0 to 3 cycle delay
            nop                         ; 3
            nop                         ; 2
            nop                         ; 1
                                        ;
            mov.b   &P1IN, ARG4         ; Get serial input          
            and     &in_bit_mask, ARG4  ;
            rrc     ARG1                ; Shift in a bit
                                        ;
            mov     ARG3, ARG2          ; Setup bit timer
            jc      rx_delay            ; Next bit...
                                        ;
            rla     ARG1                ; Move stop bit to carry
            rla     ARG1                ;>Move parity bit to carry
            addc    #0, ARG1            ;>Set LSB to parity bit
            swpb    ARG1                ;>Move rx byte to lower byte, start or parity bit in msb
; Any real parity checking could be done here, or on-the-fly during the delay cycles
            ret                         ; Return with rx char and start bit in ARG1, stop bit in carry
                                        ;       

  • oPossum and DecebaL like this

Always connect a pull-up resistor (47kΩ) and a pull-down capacitor (2.2nF) to reset.

Never use delay() or _delay_cycles(). Never use floating points in embedded systems.


#11 jazz

jazz

    Level 2

  • Members
  • 286 posts
  • LocationCroatia


Posted 30 January 2014 - 10:00 PM

Instead of breakpoint debugging I am using fast logging (http://forum.43oh.co...-hardware-tools). On MSP430F5xx USB devices by adding one extra CDC (http://forum.43oh.com/topic/2972-sbw-msp430f550x-based-programmer/?p=25923) and on MSP430x2xx devices by hardware UART (http://forum.43oh.com/topic/2972-sbw-msp430f550x-based-programmer/?p=34398).

 

Right now, working on project based on entry level MSP430x2xx devices without hardware UART, and don't like at all timer based soft UART. Code presented here is universal, with LP 9600 bps as target rate. It is too slow for debugging, and I'm not using LP locked to 9600 bps. Just removed from original code, part related to delay, and it is (almost) perfectly matched to 115200 bps with MSP430x2xx running on 1 MHz. And total code size (for UART TX) is close to nothing.

#define DBGPDIR	P1DIR
#define DBGPOUT	P1OUT
#define DBGPBIT	BIT2

;--------------------------------------------------------------------
; DbgInit
;--------------------------------------------------------------------

DbgInit		bis.b #DBGPBIT, &DBGPDIR	; TX pin
		bis.b #DBGPBIT, &DBGPOUT	; TX High
		ret

;--------------------------------------------------------------------
; DbgUartTX R15 -> Uart
;--------------------------------------------------------------------

DbgUartTX	; Byte R15 -> Uart
		; 1000000 / 115200 = 8.681  9 CLK

		push.w R14
		push.w R15
		mov.w #DBGPBIT, R14
		and.w #000FFh, R15
		add.w #00300h, R15		; Stop bit(s)
		jmp DbgUartTXLow		; Send start bit...
DbgUartTXBit	rra.w R15			; 1 Get bit to tx, test for zero
		jc DbgUartTXHigh		; 2 If high...
DbgUartTXLow	bic.b R14, &DBGPOUT		; 4 Send zero bit
		jmp DbgUartTXBit		; 2 Next bit...
DbgUartTXHigh	bis.b R14, &DBGPOUT		; 4 Send one bit
		jnz DbgUartTXBit		; 2 If tx data is not zero, then more bits to send...
		pop.w R15
		pop.w R14
		ret





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users