Menu

Tiny software serial transmitter ported to PIC

2016-12-22
2016-12-25
  • Frank Steinberg

    Frank Steinberg - 2016-12-22

    Hi

    I ported the Tiny-Software-Transmitter to PIC MC. The attached sample realizes a software UART (in fact ist an 'AT', because ist neither Universal nor a Receiver but an RS232 Asynchron Transmitter).

    Tiny means, there are only 24 words (!) added to the hex to be able to send a byte.
    - 13 lines of assembler code
    - no timers are used
    - no other hardware modules are used
    - no interrupt is used
    - 9600 - 128000 baud are possible at 16 Mhz
    - 8N1 format only
    - Tx-Pin is user configurable

    Because I'm new to ASM I need some help. The code works for me on a PIC12f1501 an a PIC18f13k50. But it lacks on 'bank selection' and 'page selection' in the asm code. Can that be a problem, if the code would be inserted in another (more complex) program? How can this be fixed? If I try to add 'banksel' in the code, GCB generates an error - why?

    Frank

    The sample:

    '''A demonstration program for GCB  - PIC only -
    '''---------------------------------------------------------------------------------
    '''This program is a super slim software serial transmitter (151 words on a PIC12f1501)
    ''' realized with 13-line asm code, PIC-Assembler so it's PIC only
    ''' tested up to 128000 baud on a 16 Mhz PIC 12f1501
    '''The pulse polarity ist designed for TTL-USB-Serial-Converters,
    ''' to invert, switch bcf / bsf (... TTX_PORT,TTX_PIN) in TTxSendByte
    '''
    '''@author  Frank Steinberg guided by the code of Ralph Doncaster:
    ''' http://nerdralph.blogspot.de/2013/12/writing-avr-assembler-code-with-arduino.html
    '''@licence GPL
    '''@version 1.0
    '''@date    21.12.2016
    '''********************************************************************************
    
    ; ----- Configuration
    #chip 12F1501, 16
    #config LVP = Off
    #option Explicit
    'OSCCON = b'01011010'     'set internal oscillator to 1 Mhz
    'OSCCON = b'01110010'     'set internal oscillator to 8 Mhz
     OSCCON = b'01111010'     'set internal oscillator to 16 Mhz
    
    ; ----- Define Hardware settings
    ;       Config Tiny Software Tx:
    ;        calculate delay-counter: TTX_DELAY=((CPUFrequency/4/BaudRate)-11)/3)
    ;         16Mhz: 7=128000 8=115200 19=57600 31=38400 43=28800 66=19200 135=9600
    ;          1Mhz: 5=9600 31=2400
    #define TTX_DELAY 8       '115200 baud
    #define TTX_PORT PORTA    'use this port to send serial data
    #define TTX_PIN 2         'use this pin to send serial data
    Dir TTX_PORT.TTX_PIN Out  '... and make it output
    Set TTX_PORT.TTX_PIN On   'set HIGH to make the first startbit recognizable
    
    ; ----- Variables
    Dim xx As Byte
    xx = 48                     'begin with a 0
    Dim TTxSVar As String * 20  'create a string
    
    ; ----- Main body of program commences here.
    
     Do Forever
    
      TTxSendByte(13)   'new line in Terminal
      TTxSendByte(10)
      TTxSendByte(">")
      TTxSendByte(xx)   'send an alternating byte
      TTxSendText(" TinyTx Software Transmitter ")    'send the text
      'TTxSVar = Str(xx) 'Str(xx) adds 278 byte code!
      'TTxSendString
      xx += 1           'alternate byte
      Wait 1 s          'time to enjoy the result
    
     Loop       'jump back to the start of the program
    
    Sub TTxSendText (In TTxText) 'Send given text bytewise
    
      Dim TTxText As String * 20 'create a string
      Dim ii As Byte
    
      For ii = 1 To TTxText(0)   'first byte in array is the string-length
       TTxSendByte(TTxText(ii))  'send via subroutine
      Next
    
    End Sub
    
    Sub TTxSendString  'Send string >TTxSVar< bytewise
    
      Dim ii As Byte
    
      For ii = 1 To TTxSVar(0)   'first byte in array is the string-length
       TTxSendByte(TTxSVar(ii))  'send via subroutine
      Next
    
    End Sub
    
    Sub TTxSendByte (In TTxDataByte)  'serial out one byte (8 databit, no paritybit, 1 stopbit)
    
       Dim TTxDlyCnt, TTxBitCnt As Byte
    
       TTxBitCnt = 10          ;10 bits to transmit (1 start + 8 data + 1 stop)
       bcf STATUS,C            ;clear CarryFlag to 0 (needed for startbit LOW)
    
      TTxLoop:
        btfss STATUS,C         ;skip next line if CarryFlag=1
        bcf TTX_PORT,TTX_PIN   ;set pin LOW if CarryFlag=0
        btfsc STATUS,C         ;skip next line if CarryFlag=0
        bsf TTX_PORT,TTX_PIN   ;set pin HIGH if CarryFlag=1
    
        TTxDlyCnt = TTX_DELAY  ;number of delay-loops
        decfsz TTxDlyCnt,1     ;decrement delaycounter, skip next line if 0
        GOTO $-1               ;loop to line above until delaycounter=0
    
        bsf STATUS,C           ;set CarryFlag to 1 (fills databyte with 8*1 - needed for stopbit HIGH)
        rrf TTxDataByte,1      ;shift next bit to CarryFlag
        decfsz TTxBitCnt,1     ;decrement bitcounter, skip next line if 0
      GOTO TTxLoop             ;process next bit until bitcounter=0
    
    End Sub
    
     

    Last edit: Frank Steinberg 2016-12-22
  • Anobium

    Anobium - 2016-12-22

    Good code, Frank. Really nice.

    Do not struggle with banksel - simply mix ASM and Great Cow BASIC.
    You assign a variable. Use GCB assignment. GCB will sort out pages andg banks.
    You need a goto. Use GCB goto. GCB will sort the correct addressing across all the chips.

     
    • Frank Steinberg

      Frank Steinberg - 2016-12-24

      Thank you for the answer.

      You assign a variable. Use GCB assignment. GCB will sort out pages andg banks.

      I wonder if is's really ruled out, that GCB adds a 'banksel'. In this case the asm code won' t work anymore, because the jumps are only to the next line. I could handle that by adding goto-jumps to labels !? But that is extra code an slows it down (slightly).

      You need a goto. Use GCB goto. GCB will sort the correct addressing across all the chips.

      How can I force that; it's the same command?

      Frank

       
  • Anobium

    Anobium - 2016-12-24

    @Frank. Which piece of code are we trying to optimise? i can try here over the Holiday period.

     
  • Anobium

    Anobium - 2016-12-24

    It is the Holiday period. :-)

     
  • Frank Steinberg

    Frank Steinberg - 2016-12-25

    It is the Holiday period. :-)

    You lucky one!
    I am filled with family and Christmas obligations until the 26th.

    Ideas for the next steps:

    • creating compatible code for IC and AVR
    • finding a smart way to detect the interrupt status (enabled or not) to restore it after sending a byte
    • to do the baud calculation by a script
    • creating a proper GCB library

    That's my last code, so the first step is mostly done (hopefully)

    '''A demonstration program for GCB  - PIC only -
    '''---------------------------------------------------------------------------------
    '''This program is a super slim software serial transmitter (151 words on a PIC12f1501)
    ''' realized with 13-line asm code, PIC-Assembler so it's PIC only
    ''' tested up to 128000 baud on a 16 Mhz PIC 12f1501
    '''The pulse polarity ist designed for TTL-USB-Serial-Converters,
    ''' to invert, switch bcf / bsf (... TTX_PORT,TTX_PIN) in TTxSendByte
    '''
    '''@author  Frank Steinberg guided by the code of Ralph Doncaster:
    ''' http://nerdralph.blogspot.de/2013/12/writing-avr-assembler-code-with-arduino.html
    '''@licence GPL
    '''@version 1.0
    '''@date    24.12.2016
    '''********************************************************************************
    
    ; ----- Configuration
    #chip 12F1501, 16
    #config LVP = Off
    #option Explicit
    'OSCCON = b'01011010'     'set internal oscillator to 1 Mhz
    'OSCCON = b'01110010'     'set internal oscillator to 8 Mhz
     OSCCON = b'01111010'     'set internal oscillator to 16 Mhz
    
    ; ----- Define Hardware settings
    ;       Config Tiny Software Tx:
    ;        calculate delay-counter: TTX_DELAY=((CPUFrequency/4/BaudRate)-13)/3)
    ;         16Mhz: 6=128000 7=115200 19=57600 31=38400 43=28800 66=19200 135=9600
    ;          1Mhz: 5=9600 31=2400
    #define TTX_DELAY 6      '115200 baud
    #define TTX_PIN PORTA.2  'use this pin to send serial data
    Dir TTX_PIN Out          '... and make it output
    Set TTX_PIN On           'set HIGH to make the first startbit recognizable
    
    ; ----- Variables
    Dim xx As Byte
    xx = 48                     'begin with a 0
    Dim TTxSVar As String * 20  'create a string
    
    ; ----- Main body of program commences here.
    
     Do Forever
    
      TTxSendByte(13)   'new line in Terminal
      TTxSendByte(10)
      TTxSendByte("T")
      TTxSendText("inyTx Software Transmitter: ")  'send a text
      TTxSendByte(xx)   'send an alternating byte
      'TTxSVar = Str(xx) 'Str(xx) adds 278 byte code!
      'TTxSendString
      xx += 1           'alternate byte
      Wait 1 s          'time to enjoy the result
    
     Loop       'jump back to the start of the program
    
    Sub TTxSendText (In TTxText) 'Send given text bytewise
    
      Dim TTxText As String * 20 'create a string
      Dim ii As Byte
    
      For ii = 1 To TTxText(0)   'first byte in array is the string-length
       TTxSendByte(TTxText(ii)) 'send via subroutine
      Next
    
    End Sub
    
    Sub TTxSendString  'Send string >TTxSVar< bytewise
    
      Dim ii As Byte
    
      For ii = 1 To TTxSVar(0)   'first byte in array is the string-length
       TTxSendByte(TTxSVar(ii))  'send via subroutine
      Next
    
    End Sub
    
    Sub TTxSendByte (In TTxDataByte)  'serial out one byte (8 databit, no paritybit, 1 stopbit)
    
      Dim TTxDlyCnt, TTxBitCnt As Byte
    
      TTxBitCnt = 10           ;10 bits to transmit (1 start + 8 data + 1 stop) [GCB]
      bcf STATUS,C             ;clear CarryFlag to 0 (needed for startbit LOW) [ASM]
    
      TTxLoop:
        ;9 cycle loop + delay per byte
        btfsc STATUS,C         ;skip next line if CarryFlag=0 [ASM]
       goto  TTx1              ;goto TTx1 if CarryFlag=1
        Set TTX_PIN Off        ;set pin LOW if CarryFlag=0 [GCB]
      TTx1:
        btfss STATUS,C         ;skip next line if CarryFlag=1 [ASM]
       goto  TTxDone           ;goto TTxDone if CarryFlag=0
        Set TTX_PIN On         ;set pin HIGH if CarryFlag=1 [GCB]
      TTxDone:
        TTxDlyCnt = TTX_DELAY  ;number of delay-loops [GCB]
      TTxDelay:
        ;delay-loop = (x cycle * delaycounter) -1
        decfsz TTxDlyCnt,F     ;decrement delaycounter, skip next line if 0 [ASM]
       goto TTxDelay           ;loop to TTxDelay until delaycounter=0
        bsf STATUS,C           ;set CarryFlag to 1 (fills databyte with 8*1 - needed for stopbit HIGH)[ASM]
        rrf TTxDataByte,F      ;shift next bit to CarryFlag [ASM]
        decfsz TTxBitCnt,F     ;decrement bitcounter, skip next line if 0 [ASM]
       goto TTxLoop            ;process next bit until bitcounter=0
    
    End Sub
    

    ... and for AVR:

    '''A demonstration program for GCB  - AVR only -
    '''--------------------------------------------------------------------------------------------------------------------------------
    '''This program is a super slim software serial transmitter (322 bytes flash)
    ''' realized with 15-line asm code, AVR-Assembler so it's AVR only
    ''' tested up to 256000 baud on an 16 or 16.5 Mhz attiny85 (digispark)
    '''The pulse logic ist designed for TTL-USB-Serial-Converters,
    ''' to invert, switch On / Off in Line 66 & 69 (Set TTX_PIN ...)
    '''
    '''@author  Frank Steinberg with the largest portion of Ralph Doncaster:
    ''' http://nerdralph.blogspot.de/2013/12/writing-avr-assembler-code-with-arduino.html
    '''@licence GPL
    '''@version 1.0
    '''@date    24.12.2016
    '''********************************************************************************
    
    ; ----- Configuration
    #chip tiny85, 16       'intended for digispark board
    #option Explicit
    
    ; ----- Define Hardware settings
    ;       Config Software Tx:
    ;       [ calculate delay-counter: TTX_DELAY=((CPUFrequency/BaudRate)-9)/3) ]
    #define TTX_DELAY 17     '16.5Mhz: 17=256000 19=230400 44=115200  '1Mhz: 5=38400 135=2400
    #define TTX_PIN PORTB.3  'use this port to send serial data
    Dir TTX_PIN Out          '... and make it output
    Set TTX_PIN On           'set HIGH to make the first startbit recognizable
    
    ; ----- Variables
    Dim TTxSVar As String * 20  'create a string
    Dim xx As Byte
    xx = 48                     'begin with a 0
    
    ; ----- Main body of program commences here.
    
     Do Forever
    
      TTxSendByte(13)   'new line in Terminal
      TTxSendByte(10)   '
      TTxSendByte("T")  'this is a valid byte too
      TTxSendText("inyTx Software Transmitter: ")  'send a text
      TTxSendByte(xx)   'send an alternating byte
      'TTxSendByte(9)    'Tab
      'TTxSVar = Str(xx) 'this adds 564 byte code!
      'TTxSendString     'this adds 2 byte code
      xx += 1           'alternate byte
      Wait 1 s          'time to enjoy the result
    
     Loop
    
    Sub TTxSendText (In TTxText)  'Send given text bytewise
    
      Dim TTxText As String * 20  'create a string
      Dim ii As Byte
    
      For ii = 1 To TTxText(0)    'first byte in array is the string-length
       TTxSendByte(TTxText(ii))   'send via subroutine
      Next
    
    End Sub
    
    Sub TTxSendString  'Send string >TTxSVar< bytewise
    
      Dim ii As Byte
    
      For ii = 1 To TTxSVar(0)   'first byte in array is the string-length
       TTxSendByte(TTxSVar(ii))  'send via subroutine
      Next
    
    End Sub
    
    Sub TTxSendByte (In TTxByte)  'serial out one byte (8 databit, no paritybit, 1 stopbit)
    
      'cli                   ;disable interrupts
      push R24              ;save register content to stack
      push R25              ;
      push R26              ;
      lds R24, TTxByte      ;load data-byte to register
      ldi R25, 10           ;load number of bits to register (1 start + 8 data + 1 stop)
      com R24               ;invert bits and set carry
      TTxLoop:
        ;9 cycle loop + delay per byte
        brcc TTx1           ;jump to TTx1 if CarryFlag=0
        Set TTX_PIN Off     ;set pin LOW if CarryFlag=1 (translated by GCB to ASM: cbi ...)
      TTx1:
        brcs TTxDone        ;jump to TTxDone if CarryFlag=1
        Set TTX_PIN On      ;set pin HIGH if CarryFlag=0 (translated by GCB to ASM: sbi ...)
      TTxDone:
        ldi R26, TTX_DELAY  ;load delaycounter to register
      TTxDelay:
        ;delay-loop = (3 cycle * delaycounter) -1
        dec R26             ;decrement delaycounter
        brne TTxDelay       ;loop to TTxDelay until delaycounter=0
        lsr R24             ;shift next bit to CarryFlag
        dec R25             ;decrement bitcounter
        brne TTxLoop        ;jump to TTxLoop and transmit next bit until bitcounter=0
      pop R26               ;restore register content from stack
      pop R25               ;
      pop R24               ;
      'sei                   ;enable interrupts
    
    End Sub
    

    Merry christmas to all

     

    Last edit: Frank Steinberg 2016-12-25

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.