Shop OBEX P1 Docs P2 Docs Learn Events
Serial output - Is there a better way to do this? — Parallax Forums

Serial output - Is there a better way to do this?

Jeff HaasJeff Haas Posts: 421
edited 2015-10-18 00:18 in Propeller 1
I'm learning Spin having moved over from PBasic, and I've got one of the inexpensive Catalex MP3 players mentioned in another thread. It works fine with this code:
{
 Catalex mp3 board sound test
 10/17/2015
 One MP3 in a folder named 01 on MicroSD card.  
}
CON
   
  _clkmode = xtal1 + pll16x         ' Crystal and PLL settings.
  _xinfreq = 5_000_000              ' 5 MHz crystal x 16 = 80 MHz

OBJ

  Serial : "FullDuplexSerial" 

PUB Go                                

  serial.start(2, 3, %0000, 9_600)

  Serial.Tx($7E)                  ' Init the Catalex MP3 player
  Serial.Tx($FF)
  Serial.Tx($06)
  Serial.Tx($0C)
  Serial.Tx($00)
  Serial.Tx($00)
  Serial.Tx($00)
  Serial.Tx($EF)
    
  waitcnt(clkfreq + cnt)            ' Wait 1 second
  waitcnt(clkfreq + cnt)            ' Wait 1 second
  
  Serial.Tx($7E)                  ' Select TF socket
  Serial.Tx($FF)
  Serial.Tx($06)
  Serial.Tx($09)
  Serial.Tx($00)
  Serial.Tx($00)
  Serial.Tx($02)
  Serial.Tx($EF)
  
  waitcnt(clkfreq + cnt)            ' Wait 1 second
  
  Serial.Tx($7E)                    ' Set volume to 15
  Serial.Tx($FF)
  Serial.Tx($06)
  Serial.Tx($06)
  Serial.Tx($00)
  Serial.Tx($00)
  Serial.Tx($0F)
  Serial.Tx($EF)
  
  waitcnt(clkfreq + cnt)            ' Wait 1 second  

  Serial.Tx($7E)                    ' Play song 1 in folder 1
  Serial.Tx($FF)
  Serial.Tx($06)
  Serial.Tx($0F)
  Serial.Tx($00)
  Serial.Tx($01)
  Serial.Tx($01)
  Serial.Tx($EF)

However, I can't figure out how to represent all the serial commands on a single line. It's easy on the BS2:
SEROUT MP3, 84, [$7E,$FF,$06,$09,$00,$00,$02,$EF]   'Select TF socket

What's the best way to do this? I tried out the other commands in FullDuplexSerial but couldn't get them to work. And I've looked through lots of the other serial methods in the OBEX but can't find what I want.

Comments

  • AribaAriba Posts: 2,690
    The str methode with a string() pseudo function does something like that, but it can not handle $00 values, because these signals the end of the string.

    One way to do it is with byte arrays in a DAT section:
    PUB Go
      ...
      CatalexCmd(@MP3Init)
      CatalexCmd(@Volume)    'set volume to 15
      ...
      Volume[6] := 3
      CatalexCmd(@Volume)    'set volume to 3
    
    
    PRI CatalexCmd(pntr) : i
      repeat i from 0 to 7
        Serial.Tx(byte[pntr][i])
    
    DAT
    MP3Init   byte  $7E,$FF,$06,$0C,$00,$00,$00,$EF
    Volume    byte  $7E,$FF,$06,$06,$00,$00,$0F,$EF
    
    As you see, you can also modify the byte data very easy before you send a command.

    Andy

  • Andy,

    Thanks! That was just what I needed. I'm still getting my head around the whole object and method-oriented way of doing things.

    FYI, after some experimentation, for anyone else interested in using the Catalex board, the MP3Init sequence is not needed. You just need these:
    DAT
    
    SelectTF      byte $7E,$FF,$06,$09,$00,$00,$02,$EF  'Select TF socket on Catalex
    Volume        byte $7E,$FF,$06,$06,$00,$00,$0F,$EF  'Set volume to 15
    PlayMP3       byte $7E,$FF,$06,$0F,$00,$01,$01,$EF  'Play song 1 in folder 1
    

    Then you can select both the MP3 to play and the folder using these commands, as Andy shows above:
    PlayMP3[5] := 2          'Select SD card folder      
    PlayMP3[6] := 1          'Select mp3 in folder
    

    Jeff
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-10-18 14:13
    Piggy-backing on Andy's examples, I would suggest creating simple method calls so that you only have to look into the details of the device once. For example:
    pub set_volume(v)
    
      Volume[6] := 0 #> v <# MAX_VOL
      CatalexCmd(@Volume)
    
  • Jeff Haas wrote: »
    I'm learning Spin having moved over from PBasic, and I've got one of the inexpensive Catalex MP3 players mentioned in another thread. It works fine with this code:
    {
     Catalex mp3 board sound test
     10/17/2015
     One MP3 in a folder named 01 on MicroSD card.  
    }
    CON
       
      _clkmode = xtal1 + pll16x         ' Crystal and PLL settings.
      _xinfreq = 5_000_000              ' 5 MHz crystal x 16 = 80 MHz
    
    OBJ
    
      Serial : "FullDuplexSerial" 
    
    PUB Go                                
    
      serial.start(2, 3, %0000, 9_600)
    
      Serial.Tx($7E)                  ' Init the Catalex MP3 player
      Serial.Tx($FF)
      Serial.Tx($06)
      Serial.Tx($0C)
      Serial.Tx($00)
      Serial.Tx($00)
      Serial.Tx($00)
      Serial.Tx($EF)
        
      waitcnt(clkfreq + cnt)            ' Wait 1 second
      waitcnt(clkfreq + cnt)            ' Wait 1 second
      
      Serial.Tx($7E)                  ' Select TF socket
      Serial.Tx($FF)
      Serial.Tx($06)
      Serial.Tx($09)
      Serial.Tx($00)
      Serial.Tx($00)
      Serial.Tx($02)
      Serial.Tx($EF)
      
      waitcnt(clkfreq + cnt)            ' Wait 1 second
      
      Serial.Tx($7E)                    ' Set volume to 15
      Serial.Tx($FF)
      Serial.Tx($06)
      Serial.Tx($06)
      Serial.Tx($00)
      Serial.Tx($00)
      Serial.Tx($0F)
      Serial.Tx($EF)
      
      waitcnt(clkfreq + cnt)            ' Wait 1 second  
    
      Serial.Tx($7E)                    ' Play song 1 in folder 1
      Serial.Tx($FF)
      Serial.Tx($06)
      Serial.Tx($0F)
      Serial.Tx($00)
      Serial.Tx($01)
      Serial.Tx($01)
      Serial.Tx($EF)
    

    However, I can't figure out how to represent all the serial commands on a single line. It's easy on the BS2:
    SEROUT MP3, 84, [$7E,$FF,$06,$09,$00,$00,$02,$EF]   'Select TF socket
    

    What's the best way to do this? I tried out the other commands in FullDuplexSerial but couldn't get them to work. And I've looked through lots of the other serial methods in the OBEX but can't find what I want.

    which thread are you referring? Just curious
  • Shawn Lowe wrote: »

    which thread are you referring? Just curious
    Could be this one:

    http://forums.parallax.com/discussion/161245/erco-here-s-the-cheap-mp3-modules

  • After looking at the docs I knocked up a little object for that player. I don't tend to post untested code (I don't have a player yet), but I'm confident this should be safe to try.

    Since I don't have one and I wanted others to be able to reference the Schmarschmino demos the vendor provides, I used their named constants. Once I get a player and understand what each thing does, I may rename things. Note, too, that all of the demos show no feedback from the player and a baud rate of 9600. This can be done with a single pin and a method; no serial cog is required.
  • It's pretty easy to create a routine that works like STR but allows for a zero byte:
    PUB txStrEsc(str) | temp
      repeat while temp := BYTE[ str++ ]  ' repeat as long as byte isn't zero
        if temp == $7F  ' escape byte is $7F
          if (temp := BYTE[ str++ ]) => $80  ' get escaped byte value
            Serial.Tx(temp & $7F)  ' strip high order bit for transmission
          else  ' A $7F must not be followed by a byte less than $80
            quit  ' This is so a string ending with $7F will terminate properly
        else
          Serial.Tx(temp)
    

    To transmit the example shown for the BS2, do:

    txStrEsc( string( $7E, $FF, $06, $09, $7F, $80, $7F, $80, $02, $EF ) )

    A $7F is followed by the byte to be sent with its high order bit set. To send $00, use $7F,$80. To send $7F, use $7F,$FF. There's a check for the case where a $7F is the last byte in a string.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2015-10-18 18:08
    For this common situation add a method to your serial object. Enter with a pointer to the string and the count of bytes.
    PUB StrN(stringptr,nchar)
    '' Send counted string
      repeat nchar
        tx(byte[stringptr++])
    
    Then as Jon suggests, in the application give each function a meaningful name that calls the generic StrN. Or, simply give each string a name that reveals its function.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2015-10-18 18:15
    Actually, looking at Jon's program, I see that the device command set has several constant elements and two variable elements, and Jon has encapsulated that in a method,
    send_command(cmd, value)
    
    Very much on target, as usual.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-10-18 19:12
    To be fair, I simply ported code from the vendor's Schmarschmino demo. Here's the original.
    void sendCommand(int8_t command, int16_t dat)
    {
      delay(20);
      Send_buf[0] = 0x7e; //
      Send_buf[1] = 0xff; //
      Send_buf[2] = 0x06; //
      Send_buf[3] = command; //
      Send_buf[4] = 0x00;//
      Send_buf[5] = (int8_t)(dat >> 8);//datah
      Send_buf[6] = (int8_t)(dat); //datal
      Send_buf[7] = 0xef; //
      for(uint8_t i=0; i<8; i++)//
      {
        mySerial.write(Send_buf[i]) ;
      }
    }
    

    I didn't think it was necessary to stick the bytes into a buffer before sending -- at 9600 baud a few microseconds here and there between bytes should be fine.
    pri send_command(cmd, value)
    
      delay(20)
      tx($7E)
      tx($FF)
      tx($06)
      tx(cmd)
      tx($00)
      tx(value.byte[1])
      tx(value.byte[0])
      tx($EF)
    
    
    pri delay(ms) | t
    
      t := cnt
      repeat ms
        waitcnt(t += clkfreq/1000)
    
    
    pri tx(b) | t                                           ' borrowed from Simple Serial
    
    '' True mode output of byte b
    
      b := (b | $FF00) << 2                                 ' add stop bit(s), start bit
    
      t := cnt                                              ' sync bit timer
      repeat 10                                             ' start + 8 bits + stop
        waitcnt(t += bitticks)                              ' hold for bit timing
        outa[txp] := b >>= 1                                ' output next bit
    

    That demo commands disable response from the device so I kept it to one pin. Since coms is the very tame speed of 9600 baud, I was able to borrow (with some clean-up) serial output from Simple Serial.
  • I tried out JonnyMac's port of the C code, by using jm_debounce to read the buttons and then send commands to jm_catalex_mp3. It works!

    I set up six buttons as the schematic in the original file here in the OBEX shows. Those aren't shown below because some characters don't display properly in the forum.
    '' =================================================================================================
    ''
    ''   File....... jm_debounce_catalex_mp3.spin
    ''   Purpose.... Debounces inputs, sends commands to Catalex MP3 player
    ''   Updated.... 20 OCT 2015
    ''
    '' =================================================================================================
    
    con
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000 
    
    
    con
    
      BTN_UP   = %000001 << 18                                       ' for JM LCD UI board 
      BTN_DN   = %000010 << 18
      BTN_LF   = %000100 << 18
      BTN_RT   = %001000 << 18
      BTN_OK   = %010000 << 18
      BTN_ESC  = %100000 << 18
    
      BTN_MASK = BTN_UP | BTN_DN | BTN_RT | BTN_LF | BTN_OK | BTN_ESC
    
      ACT_HI   = 1
      ACT_LO   = 0 
    
    
    obj
    
      btns : "jm_debounce"
      catalex  : "jm_catalex_mp3"
    
    var
    
    
    pub main | b
    
      catalex.setup(3)                            ' TX pin to Catalex is pin 3  
        
      btns.init(BTN_MASK, 25, ACT_HI)
    
      repeat
        if btns.getbits(BTN_ESC)
          catalex.stop
    
        b := btns.getbits(BTN_MASK) & !BTN_ESC                      ' all but Esc
        case b
          BTN_UP : catalex.play_from_folder(2,1)               ' LCD commands in original replaced with these commands for testing
          BTN_DN : catalex.next_song 
          BTN_LF : catalex.previous_song
          BTN_RT : catalex.volume_up
          BTN_OK : catalex.volume_down 
        
    
        waitcnt(clkfreq / 10 + cnt)
    
    
Sign In or Register to comment.