Jump to content
43oh

DecebaL

Members
  • Content Count

    2
  • Joined

  • Last visited

Reputation Activity

  1. Like
    DecebaL reacted to Rickta59 in Software async serial tx/rx without timer   
    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 ;------------------------------------------------------------------------------- ; --- 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
  2. Like
    DecebaL reacted to oPossum in Software async serial tx/rx without timer   
    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(; { // 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 ;
  3. Like
    DecebaL reacted to roadrunner84 in Software async serial tx/rx without timer   
    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 ;
×
×
  • Create New...