' ========================================================================= ' ' File...... SC01TOSJ_POLLED.SXB ' Purpose... Convert SC-01 to SpeakJet ' Author.... (c) Robert L Doerr -- All Rights Reserved ' E-mail.... rdoerr@bizserve.com ' Website... http://www.robotworkshop.com ' Created... 03 JAN 2007 ' Updated... 24 MAY 2007 ' ' Version... 1.5 ' ' ========================================================================= ' ------------------------------------------------------------------------- ' Program Description ' ------------------------------------------------------------------------- ' ' This program is written for an SX28 processor which is used to interface ' a SpeakJet speech chip to an existing device that would normally use the ' now obsolete (and hard to find) SC-01 speech chip. The program and processor ' handle the difference in hardware protocol (parallel vs serial), handles ' all the handshaking and control signals, and also translates the SC-01 ' phoneme code to the appropriate SpeakJet phoneme code. This will allow ' a SpeakJet to be used in place of an SC-01 chip without requiring any ' modifications to the host software or hardware. ' ' See schematic for wiring. Note that while the SC-01 has 5v compatible ' latched inputs, it may have a higher supply voltage (typically 12V) and ' will require a voltage regulator on the supply pin to bring it down ' to 5V or a separate 5V supply for power. ' ' Future enhancements will be as follows: ' ' - Improve/tweak phoneme translation table to improve translation ' - Use the RCTIME command to check the adjustable RC circuit used to set ' the Master clock for the SC-01. This will allow codes to be sent to the ' SpeakJet to change the overall speed to make it act like a real SC-01. ' - More to follow..... ' ------------------------------------------------------------------------- ' Device Settings ' ------------------------------------------------------------------------- ' Configure SX28 options and setup to use OSCXT2 (external 4mhz oscillator) DEVICE SX28, OSCXT2, TURBO, STACKX, OPTIONX FREQ 4_000_000 ID "SC01TOSJ" ' ------------------------------------------------------------------------- ' IO Pins ' ------------------------------------------------------------------------- SJSer VAR RA.0 ' Serial output to SpeakJet SJRdy VAR RA.1 ' SpeakJet Ready line SJSpk VAR RA.2 ' Is it Speaking?? SJBusy VAR RA.3 ' Buffer Half Full (from SpeakJet) SIO VAR TRIS_A ' I/O direction of Port A SC01Clk VAR RB.1 ' SC-01 R/C Clock speed adjustment DIP1 VAR RB.2 ' DIP switch 1 - SC-01 or Alternate R2 sounds mode DIP2 VAR RB.3 ' DIP switch 2 - Enable extra debug info DIP3 VAR RB.4 ' DIP switch 3 - Setup/Configure new SpeakJet chip ' Only use to initially setup new SpeakJet. Turn off ' for normal use to prevent wearing out the SpeakJet EEPROM. DbgSer VAR RB.5 ' Transmit only Debug output (9600 baud via MAX232) PStb VAR RB.7 ' SC-01 latch signal (low to high transition) PBusy VAR RB.6 ' SC-01 A/R (low=busy, high=ok for more) CIO VAR TRIS_B ' I/O direction of Port B (Control port) PData VAR RC ' Phoneme and Pitch data PIO VAR TRIS_C ' I/O direction of Port C ' ------------------------------------------------------------------------- ' Constants ' ------------------------------------------------------------------------- True CON 1 False CON 0 Baud CON "T9600" ' Baud rate for SpeakJet BaudDbg CON "T9600" ' Baud rate for Debug ' ------------------------------------------------------------------------- ' Variables ' ------------------------------------------------------------------------- phonemeRaw VAR Byte ' Phoneme Code phoneme VAR Byte ' Actual Phoneme phonemeTable VAR Word ' Pointer to active phoneme table phonemeIndex VAR Word ' Index to lookup phoneme phonemeOffset VAR Byte ' Offset to next phoneme pitch VAR Byte ' Pitch data (0, 1, 2, or 3) lastPitch VAR Byte ' Last known Pitch value lastPhoneme VAR Byte ' Last phoneme code sent to SJ tmpB1 VAR Byte ' subroutine and temp work vars tmpB2 VAR Byte tmpW1 VAR Word ' ------------------------------------------------------------------------- PROGRAM Start ' ------------------------------------------------------------------------- ' ------------------------------------------------------------------------- ' Function and Subroutine Declarations ' ------------------------------------------------------------------------- TX_Byte SUB 1 ' TX to Serial I/O TX_ByteDbg SUB 1 ' Transmit character to debug port TX_ByteDbgHex SUB 1 ' Transmit Hex value to debug port DELAY SUB 1, 2 ' delay in milliseconds ' ------------------------------------------------------------------------- ' Program Code ' ------------------------------------------------------------------------- ' Setup IO ports: Direction, etc. Start: SIO = %00001110 ' Set RA.0 as output for SJ CIO = %10011111 ' Setup SC-01 Control port PIO = %11111111 ' Setup Phoneme/Pitch port LOW PBusy ' Say that SC-01 is busy..... lastPitch = 0 ' Last pitch from host (set to default of SJ) lastPhoneme = 0 ' Last phoneme used (set to range outside ) ' Send out version string to debug port phonemeIndex = 0 DO READ Dbg_Prompt+phonemeIndex, phoneme IF phoneme = 0 THEN Prompt_Done phonemeIndex = phonemeIndex +1 TX_ByteDbg phoneme LOOP Prompt_Done: ' Send out status of DIP switch settings to debug port TX_ByteDbg "1" ' Switch 1 IF DIP1 = True THEN TX_ByteDbg "T" ELSE TX_ByteDbg "F" ENDIF TX_ByteDbg "2" ' Switch 2 IF DIP2 = True THEN TX_ByteDbg "T" ELSE TX_ByteDbg "F" ENDIF TX_ByteDbg "3" ' Switch 3 IF DIP3 = True THEN TX_ByteDbg "T" ELSE TX_ByteDbg "F" ENDIF TX_ByteDbg 13 ' Send out CR/LF to debug port TX_ByteDbg 10 ' Wait until SpeakJet is ready... SJ_Init: IF SJRdy = False THEN GOTO SJ_Init ENDIF TX_Byte 0 ' Send out short pause to SpeakJet TX_Byte 4 IF DIP3 = True THEN Setup_Done ' Skip setup of SpeakJet if not new chip ' Send out special codes to configure a new SpeakJet chip (disable "READY") phonemeIndex = 0 DO READ Config_SJ+phonemeIndex, phoneme IF phoneme = 0 THEN Setup_Done phonemeIndex = phonemeIndex +1 TX_Byte phoneme PAUSE 5 LOOP Setup_Done: HIGH PBusy ' Ok, we're ready to go.... ' This is the main loop for the SC-01 translator... Main: IF PStb = False THEN GOTO Main ' No phoneme/pitch from Host yet... ENDIF phonemeRaw = PData ' Get Phoneme/Pitch from Port C LOW PBusy ' Tell Host that Phoneme received Host_Ready: IF PStb = True THEN GOTO Host_Ready ' Wait until host is done with this code. ENDIF SJ_Busy: IF SJBusy = True THEN GOTO SJ_Busy ELSEIF SJRdy = False THEN GOTO SJ_Busy ' Hold until SpeakJet ready to accept data ENDIF IF DIP2 = False THEN TX_ByteDbgHex phonemeRaw ' Send HEX value of phoneme/pitch to debug port ENDIF ' Separate Phoneme and Pitch data... phoneme = phonemeRaw & %00111111 ' Get just the phoneme IF DIP2 = False THEN TX_ByteDbgHex phoneme ' Send HEX value of phoneme only to debug port ENDIF pitch = phonemeRaw & %11000000 ' Get the pitch bits pitch = pitch >> 6 ' Shift them over to the proper bits ' Routine to send out pitch data for each phoneme.. (Only if different) IF pitch <> lastPitch THEN lastPitch = pitch LOOKUP pitch, 88, 100, 124, 172, pitch ' Convert to SJ pitch value TX_Byte 22 ' Set pitch command TX_Byte pitch ' Send the value of the new pitch. ENDIF ' Set default phoneme table for SC-01 to SpeakJet conversion. phonemeTable=Phoneme_Map ' Check for translation mode (SC-01 or Alternate) IF DIP1 = False THEN phonemeTable=R2_Map ' Switch to SC-01 to R2 phoneme table ENDIF READ phonemeTable, phonemeOffset ' Get the # bytes per SC-01 phoneme. ' Calculate pointer to first byte of code to send out phonemeIndex = phoneme * phonemeOffset phonemeIndex = phonemeIndex+phonemeTable ' Add one to account for byte of offset data INC phonemeIndex ' Get and send out the actual phoneme.. Send_Code: READ phonemeIndex, tmpB1 ' Get the SJ code.. IF tmpB1 > 0 THEN ' Check to see if end of codes. phoneme=tmpB1 TX_Byte phoneme ' Send to SpeakJet IF DIP2 = False THEN TX_ByteDbgHex phoneme ' Send HEX value to debug port of code sent to SpeakJet chip ENDIF INC phonemeIndex ' Point to next code DEC phonemeOffset ' One less to process IF phonemeOffset>0 THEN GOTO Send_Code ENDIF ENDIF Done_Code: IF DIP2 = False THEN TX_ByteDbg 13 ' Send out CR/LF to tidy up output on debug port TX_ByteDbg 10 ENDIF IF phoneme > 63 THEN No_Stop IF DIP2 = False THEN TX_ByteDbg "." ' Send out period & CR/LF on debug port to highlight STOP TX_ByteDbg 13 TX_ByteDbg 10 ENDIF ' Since we had a STOP phoneme code wait till done speaking. Wait_Spk: IF SJSpk = True THEN GOTO Wait_Spk ENDIF No_Stop: HIGH PBusy ' Tell Host we're ready for another phoneme. GOTO Main ' ------------------------------------------------------------------------- ' Subroutine Code ' ------------------------------------------------------------------------- ' ------------------------------------------------------------------------- ' Use: DELAY ms ' -- 'ms' is delay in milliseconds, 1 - 65535 DELAY: IF __PARAMCNT = 1 THEN tmpW1 = __PARAM1 ' save byte value ELSE tmpW1 = __WPARAM12 ' save word value ENDIF PAUSE tmpW1 RETURN ' ------------------------------------------------------------------------- ' Use: TX_Byte char ' -- 'Transmits 'char' over serial connection TX_Byte: tmpB1 = __PARAM1 ' get the passed byte value SEROUT SJSer, Baud, tmpB1 ' Send the byte RETURN ' ------------------------------------------------------------------------- ' Use: TX_ByteDbg char ' -- 'Transmits 'char' over serial connection TX_ByteDbg: tmpB1 = __PARAM1 ' get the passed byte value SEROUT DbgSer, BaudDbg, tmpB1 ' Send the byte RETURN ' ------------------------------------------------------------------------- ' Use: TX_ByteDbgHex char ' -- 'Transmits 'char' over serial connection TX_ByteDbgHex: tmpB1 = __PARAM1 ' get the passed byte value tmpB2 = tmpB1 // 16 ' Get the low nybble digit (0-15) tmpB1 = tmpB1 / 16 ' Get the high nybble digit (0-15) tmpB1 = tmpB1 +$30 IF tmpB1 > $39 THEN tmpB1 = tmpB1 + 7 ' Adjust ASCII for A-F ENDIF tmpB2 = tmpB2 +$30 IF tmpB2 > $39 THEN tmpB2 = tmpB2 + 7 ' Adjust ASCII for A-F ENDIF SEROUT DbgSer, BaudDbg, tmpB1 ' Send the upper nybble SEROUT DbgSer, BaudDbg, tmpB2 ' Send the lower nybble SEROUT DbgSer, BaudDbg, 32 ' Send the trailing space RETURN ' ========================================================================= ' User Data ' ========================================================================= ' Conversion Lookup table to translate SC-01 Phoneme code to similar ' SpeakJet Allophoneme code(s). ' Table structure is fixed 4 bytes per entry (for fast indexing): ' Code 1, Code 2, Code 3, and Code 4. A zero or 4th Code ends entry. Phoneme_Map: ' Specify # of bytes per phoneme code DATA 4 ' Reserve 4 bytes for each SC-01 phoneme code ' Start of phoneme conversion table DATA 131,0,0,0 ' $00 - EH3 (59ms) ?? DATA 131,0,0,0 ' $01 - EH2 (71ms) ?? DATA 131,131,0,0 ' $02 - EH1 (121ms) DATA 5,0,0,0 ' $03 - PA0 (47ms) DATA 174,0,0,0 ' $04 - DT (47ms) DATA 154,0,0,0 ' $05 - A2 (71ms) ?? DATA 154,0,0,0 ' $06 - A1 (103ms) ?? DATA 168,0,0,0 ' $07 - ZH (90ms) DATA 135,0,0,0 ' $08 - AH2 (71ms) DATA 131,0,0,0 ' $09 - IH3 (55ms) DATA 129,0,0,0 ' $0A - I2 (80ms) ?? DATA 129,0,0,0 ' $0B - I1 (121ms) ?? DATA 140,0,0,0 ' $0C - M (103ms) DATA 142,0,0,0 ' $0D - N (80ms) DATA 170,0,0,0 ' $0E - B (71ms) DATA 166,0,0,0 ' $0F - V (71ms) DATA 182,0,0,0 ' $10 - CH (71ms) DATA 189,189,0,0 ' $11 - SH (121ms) DATA 167,0,0,0 ' $12 - ZH (71ms) DATA 136,136,0,0 ' $13 - AW1 (146ms) DATA 143,143,0,0 ' $14 - NG (121ms) DATA 136,0,0,0 ' $15 - AH1 (146ms) DATA 138,0,0,0 ' $16 - OO1 (103ms) DATA 139,0,0,0 ' $17 - OO (185ms) DATA 145,0,0,0 ' $18 - L (103ms) DATA 194,0,0,0 ' $19 - K (80ms) DATA 165,0,0,0 ' $1A - J (47ms) DATA 183,0,0,0 ' $1B - H (71ms) ?? (maybe 184) DATA 178,0,0,0 ' $1C - G (71ms) DATA 186,0,0,0 ' $1D - F (103ms) DATA 176,0,0,0 ' $1E - D (55ms) DATA 188,0,0,0 ' $1F - S (90ms) DATA 154,0,0,0 ' $20 - A (185ms) DATA 130,0,0,0 ' $21 - AY (65ms) DATA 158,0,0,0 ' $22 - Y1 (80ms) DATA 155,0,0,0 ' $23 - UH1 (47ms) DATA 133,0,0,0 ' $24 - AH (250ms) ?? DATA 198,0,0,0 ' $25 - P (103ms) DATA 164,0,0,0 ' $26 - O (185ms) DATA 129,0,0,0 ' $27 - I (185ms) DATA 139,0,0,0 ' $28 - U (185ms) DATA 128,0,0,0 ' $29 - Y (103ms) DATA 191,0,0,0 ' $2A - T (71ms) DATA 148,0,0,0 ' $2B - R (90ms) DATA 128,128,0,0 ' $2C - E (185ms) DATA 147,0,0,0 ' $2D - W (80ms) DATA 132,132,0,0 ' $2E - AE (185ms) ?? DATA 132,0,0,0 ' $2F - AE1 (103ms) DATA 133,0,0,0 ' $30 - AW2 (90ms) DATA 134,0,0,0 ' $31 - UH2 (71ms) DATA 134,134,0,0 ' $32 - UH1 (103ms) DATA 161,0,0,0 ' $33 - UH (185ms) ?? DATA 137,0,0,0 ' $34 - O2 (80ms) DATA 153,0,0,0 ' $35 - O1 (121ms) DATA 162,0,0,0 ' $36 - IU (59ms) DATA 139,0,0,0 ' $37 - U1 (90ms) (was 160) DATA 169,0,0,0 ' $38 - THV (80ms) DATA 190,0,0,0 ' $39 - TH (71ms) DATA 151,0,0,0 ' $3A - ER (146ms) DATA 131,131,131,0 ' $3B - EH (185ms) DATA 128,128,0,0 ' $3C - E1 (121ms) ?? DATA 136,136,136,0 ' $3D - AW (250ms) ?? DATA 2,0,0,0 ' $3E - PA1 (185ms) DATA 5,0,0,0 ' $3F - STOP (47ms) ' Conversion Lookup table to translate SC-01 Phoneme code to similar ' R2 sounding Allophoneme code. R2_Map: ' Specify # of bytes per phoneme code DATA 1 ' Use 1 byte per SC-01 phoneme code ' Start of phoneme conversion table DATA 200 ' $00 - EH3 (59ms) DATA 200 ' $01 - EH2 (71ms) DATA 200 ' $02 - EH1 (121ms) DATA 5 ' $03 - PA0 (47ms) DATA 203 ' $04 - DT (47ms) DATA 204 ' $05 - A2 (71ms) DATA 204 ' $06 - A1 (103ms) DATA 250 ' $07 - ZH (90ms) DATA 207 ' $08 - AH2 (71ms) DATA 208 ' $09 - IH3 (55ms) DATA 226 ' $0A - I2 (80ms) DATA 226 ' $0B - I1 (121ms) DATA 221 ' $0C - M (103ms) DATA 222 ' $0D - N (80ms) DATA 223 ' $0E - B (71ms) DATA 239 ' $0F - V (71ms) DATA 225 ' $10 - CH (71ms) DATA 226 ' $11 - SH (121ms) DATA 251 ' $12 - ZH (71ms) DATA 228 ' $13 - AW1 (146ms) DATA 229 ' $14 - NG (121ms) DATA 207 ' $15 - AH1 (146ms) DATA 220 ' $16 - OO1 (103ms) DATA 220 ' $17 - OO (185ms) DATA 227 ' $18 - L (103ms) DATA 202 ' $19 - K (80ms) DATA 205 ' $1A - J (47ms) DATA 206 ' $1B - H (71ms) DATA 208 ' $1C - G (71ms) DATA 225 ' $1D - F (103ms) DATA 209 ' $1E - D (55ms) DATA 244 ' $1F - S (90ms) DATA 221 ' $20 - A (185ms) DATA 205 ' $21 - AY (65ms) DATA 240 ' $22 - Y1 (80ms) DATA 201 ' $23 - UH1 (47ms) DATA 207 ' $24 - AH (250ms) DATA 223 ' $25 - P (103ms) DATA 227 ' $26 - O (185ms) DATA 228 ' $27 - I (185ms) DATA 229 ' $28 - U (185ms) DATA 240 ' $29 - Y (103ms) DATA 205 ' $2A - T (71ms) DATA 208 ' $2B - R (90ms) DATA 222 ' $2C - E (185ms) DATA 211 ' $2D - W (80ms) DATA 224 ' $2E - AE (185ms) DATA 223 ' $2F - AE1 (103ms) DATA 207 ' $30 - AW2 (90ms) DATA 200 ' $31 - UH2 (71ms) DATA 200 ' $32 - UH1 (103ms) DATA 208 ' $33 - UH (185ms) DATA 205 ' $34 - O2 (80ms) DATA 228 ' $35 - O1 (121ms) DATA 238 ' $36 - IU (59ms) DATA 242 ' $37 - U1 (90ms) DATA 204 ' $38 - THV (80ms) DATA 212 ' $39 - TH (71ms) DATA 221 ' $3A - ER (146ms) DATA 222 ' $3B - EH (185ms) DATA 223 ' $3C - E1 (121ms) DATA 233 ' $3D - AW (250ms) DATA 2 ' $3E - PA1 (185ms) DATA 5 ' $3F - STOP (47ms) Config_SJ: DATA "\0237J32HF0NX",0 ' SpeakJet codes to disable "READY" Dbg_Prompt: DATA "SC-01 to SpeakJet translator (Version 1.5):",13,10 DATA "DIP Switch status: ",0