Interesting. I never noticed that one before until looking at where he hadOriginally Posted by Darrel Taylor
Clr instead of clear.
Interesting. I never noticed that one before until looking at where he hadOriginally Posted by Darrel Taylor
Clr instead of clear.
Great job Darrel! It just keeps getting better and better.
Here is the tested version of the code incorporating your changes:
So now we have a single line RS232 messaging system that will work properly, assuming you don't use any interrupts in the rest of your code (that's right, this will probably fail with interrupts enabled).Code:DEFINE LOADER_USED 1 DEFINE OSC 40 ' change to suit oscillator speed setSPBRG con 129 ' 19200 baudrate w/ 40Mhz OSC ' (change to suit requirements) temp var byte ' temporary storage for character Goto main ' Send String Macro (must be located prior to Main) ASM SendStr macro Astring call _prntSTR data Astring,0 endm ENDASM '======================================= Main: CALL USART_Init ' initialize hardware USART ' send messages through RS232 ' format #1 @ SendStr "This is a string" @ SendStr "yet another string again" @ SendStr "and one more time!" ' format #2 ASM SendStr "This is a string" SendStr "yet another string again" SendStr "and one more time!" ENDASM Done: Goto Done ' loop forever '======================================= ' USART initialization and print string code (locate anywhere) asm _USART_Init movlw B'00100100' ; initialize USART movwf TXSTA ; 8-bit, Async, High Speed movlw _setSPBRG ; pass constant to SPBRG to movwf SPBRG ; determine baudrate to be used movlw B'10010000' ; enable USART movwf RCSTA return _prntSTR ;copy return address to TBLPTR and then pop it off the stack movf TOSU,W movwf TBLPTRU movf TOSH,W movwf TBLPTRH movf TOSL,W movwf TBLPTRL POP ;TBLPTR should now be pointing to 1st character in string Next_Char tblrd *+ ; table read and post increment TBLPTR movff TABLAT,_temp ; fetch character from message string bra Test_EOM ; go test for EOM character Continue ; If not EOM then... movf _temp,W ; move character into TXREG and... movwf TXREG ; send it! btfss TXSTA,TRMT ; has it been transmitted? goto $-2 ; If not, keep checking bra Next_Char ; fetch next message character from table Test_EOM movlw .0 ; check for EOM character cpfseq _temp,W ; compare temp with w, if temp = 0 then end bra Continue ; no EOM, so continue movlw "\r" ; move carriage return into TXREG and... movwf TXREG ; send it! btfss TXSTA,TRMT ; has it been transmitted? goto $-2 ; If not, keep checking movlw "\n" ; move line feed into TXREG and... movwf TXREG ; send it! btfss TXSTA,TRMT ; has it been transmitted? goto $-2 ; If not, keep checking ;use incremented value of TBLPTR as new return address (push it) PUSH movf TBLPTRU,W movwf TOSU movf TBLPTRH,W movwf TOSH movf TBLPTRL,W movwf TOSL return ;finished with message, return to caller endasm
Now to get the interrupt compatible version we'll need an efficient way to disable global interrupts when doing the stack manipulations. Since there are 2 levels of interrupts (GIEL and GIEH) possible on an 18 series pic it's a little more complicated. Before I dive into this does anyone have a good suggestion or example code?
Thanks,
If you look at the last code example you may be asking yourself "Hey isn't that the same thing as HSEROUT ["This is a string"]?"
Well... yes it is, sort of. What I really see to be accomplished here, has more to do with creating the framework for a customizable message string handling Function, then just mimicing an already available PicBasic Pro function. For testing purposes, it is just easier to send it out unaltered through the RS232 port. Ulimately, it will hopefully evolve into a great way to send either message strings, data, or both to any device you wish. While at the same time doing it in a way that is more like a built-in PicBasic Pro function.
A great example of what this could be used for, is imagine that you are interfaced to a device that would accept message strings via RS232, but always requires special commands to precede the message. Using what has already been developed here, all that would be required is to add these commands within the printSTR code (same as what was done for the carriage return and linefeed). Then every time you send a message such as @ SendStr "This is a string" the pre commands would be automatically sent as well.
Even better still, imagine if you were interfaced to several different devices or gateways, each one having special communication requirements (I2C, SPI, One-Wire, RS232, ect.). Every protocol could concievably be supported within the printSTR code, thereby allowing you to broadcast simultaneously to all devices via the single line SendStr function. Pretty cool huh!
We are only scratching the surface as to the possibilities.
Last edited by mytekcontrols; - 1st July 2005 at 20:29. Reason: Just had another thought
Hiya Michael,
And for my next installment of things you can change with the program you already have working ....
Sometimes I go crazy with the macro's. But I kinda like this one.Now to initialize the USART all you have to do is this...Code:'--[ Initialise USART for specified baud rate at current OSC speed ]------------ ASM USART_Init macro Baud clrf TXSTA, 0 _SPBRG = (OSC * 1000000) / 16 / Baud - 1 ; calc SPBRG @ High baud rate if _SPBRG > 255 ; if SPBRG is too high _SPBRG = (OSC * 1000000) / 64 / Baud - 1 ; calc for Low baud rate bcf TXSTA, BRGH, 0 ; Set BRGH to Low Speed if _SPBRG > 255 _SPBRG = 255 endif else bsf TXSTA, BRGH, 0 ; Set BRGH to High Speed endif bsf TXSTA, TXEN, 0 ; Set Transmit Enable bit movlw _SPBRG movwf SPBRG, 0 ; load the calulated SPBRG movlw B'10010000' ; enable USART movwf RCSTA, 0 endm ENDASMIt should work with any OSC setting and any valid baud rate.Code:@ USART_Init 19200
-------------------------
>> Ulimately, it will hopefully evolve into a great way to send either message strings, data, or both to any device you wish.
I've also got some MAJOR changes that allow you to do the above quote. Haven't tested them completely yet, but they look promising.
Best regards,
Darrel
Hey Darrel, I really think we got you hooked on this :-)I've also got some MAJOR changes that allow you to do the above quote. Haven't tested them completely yet, but they look promising.
Which is perfectly fine by me, and will surely be fun to see what's in store.
Any good ideas on the best way to deal with the interrupt disable issue? I was reading up on this a bit, and it seems like some approaches could cause latency problems. I am also not 100% sure of which parts of the stack manipulation are no no's while an interrupt is occuring (in otherwords, is it only during the PUSH, or POP or both).
My particular interest will be to implement the "printSTR" in an I2C format, since a video chip I am working with requires this. It also has fussy timing requirements that appear to lower than even the standard device speed. to top it off I have 2 chips on board, so I am using independant clock lines and a shared SDA, since they both have identical addresses. Presently I have everything working with a custom PicBasic Pro I2C routine, but I suspect I'll need to implement this as an ASM routine if it is to be incorporated into the "printSTR" routine. Unless your MAJOR changes would allow for this.
BTW: I like the USART routine, it makes it very easy which we all love.
Darrel this incorporates your USART macro (works great):
Code:DEFINE LOADER_USED 1 ' use for Boot Loader DEFINE OSC 40 ' change to suit oscillator speed '============================================ ' Equates '============================================ temp var byte ' temporary storage for character Goto main ' skip around Macros '============================================ ' Macros '============================================ ' Send String Macro (must be located prior to Main) ASM SendStr macro Astring call _prntSTR data Astring,0 endm ENDASM ' USART Initialization Macro (must be located prior to Main) ' [ Initialize USART for specified baud rate at current OSC speed ] ASM USART_Init macro Baud clrf TXSTA, 0 _SPBRG = (OSC * 1000000) / 16 / Baud - 1 ; calc SPBRG @ High baud rate if _SPBRG > 255 ; if SPBRG is too high _SPBRG = (OSC * 1000000) / 64 / Baud - 1 ; calc for Low baud rate bcf TXSTA, BRGH, 0 ; Set BRGH to Low Speed if _SPBRG > 255 _SPBRG = 255 endif else bsf TXSTA, BRGH, 0 ; Set BRGH to High Speed endif bsf TXSTA, TXEN, 0 ; Set Transmit Enable bit movlw _SPBRG movwf SPBRG, 0 ; load the calulated SPBRG movlw B'10010000' ; enable USART movwf RCSTA, 0 endm ENDASM '============================================ ' Main Program '============================================ Main: @ USART_Init 19200 ; set RS232 baudrate ' send messages through RS232 ' format #1 @ SendStr "This is a string" @ SendStr "yet another string again" @ SendStr "and one more time!" ' format #2 ASM SendStr "This is a string" SendStr "yet another string again" SendStr "and one more time!" ENDASM Done: Goto Done ' loop forever '============================================ ' String Extraction and Print Routines '============================================ ' print string code (locate anywhere) ASM _prntSTR ;copy return address to TBLPTR and then pop it off the stack movf TOSU,W movwf TBLPTRU movf TOSH,W movwf TBLPTRH movf TOSL,W movwf TBLPTRL POP ;TBLPTR should now be pointing to 1st character in string Next_Char tblrd *+ ; table read and post increment TBLPTR movff TABLAT,_temp ; fetch character from message string bra Test_EOM ; go test for EOM character Continue ; If not EOM then... movf _temp,W ; move character into TXREG and... movwf TXREG ; send it! btfss TXSTA,TRMT ; has it been transmitted? goto $-2 ; If not, keep checking bra Next_Char ; fetch next message character from table Test_EOM movlw .0 ; check for EOM character cpfseq _temp,W ; compare temp with w, if temp = 0 then end bra Continue ; no EOM, so continue movlw "\r" ; move carriage return into TXREG and... movwf TXREG ; send it! btfss TXSTA,TRMT ; has it been transmitted? goto $-2 ; If not, keep checking movlw "\n" ; move line feed into TXREG and... movwf TXREG ; send it! btfss TXSTA,TRMT ; has it been transmitted? goto $-2 ; If not, keep checking ;use incremented value of TBLPTR as new return address (push it) PUSH movf TBLPTRU,W movwf TOSU movf TBLPTRH,W movwf TOSH movf TBLPTRL,W movwf TOSL return ;finished with message, return to caller ENDASM
That's great Michael, glad you liked it. And it's good to have willing particpants around.
And, if you wanted more macro's... then I'm sure to make you happy today.
Oh boy, where to start.... I guess I'll start at the end result.
What this does is to allow the strings to be sent to up to 7 different "Destinations". Those destinations can be easily defined like this.This creates a Destination named "HSER", and everytime the program needs to send a character, it will call the _HSER_Send subroutine.Code:@ Make_Destination HSER, _HSER_Send
Each destination needs it's own subroutine to actually send the data. The character is placed in the temp variable before calling it. Here's one for HSER.To make another Destination for the LCD, is just as easy...Code:HSER_Send: While PIR1.4 = 0 : Wend TXREG = temp returnNow when using the SendStr macro, you can tell it which destination you want it to go to.Code:@ Make_Destination LCD, _LCD_Send LCD_Send: LCDOUT temp Return
@ SendStr "This is a string", HSER
@ SendStr "1 for the LCD", LCD
You can even combine them to send the data to multiple Destinations.
@ SendStr "This is a string", HSER + LCD
Destination #8 is reserved for a CR,LF option that can be used like this...
@ SendStr "This is a string", HSER + CRLF
Of course, here's the one you were after...In order to make this work, I ripped up your code pretty bad. Sorry!Code:@ Make_Destination I2C, _I2C_Send I2C_Send: ' However you need to do it here... RETURN
For instance, I got rid of the whole Pushing and Popping stuff. I hated to do that, but at least it solves the interrupt problem. The TBLPTR is now loaded from the SendStr macro so that it doesn't need to use the stack to get the address. Major changes to the prntSTR section too. It had all the USART stuff in there that had to come out. The Make_Destination and Director macro's are the real difference that allows the data to be re-directed. I'm sure there will be some questions on them, but for now I'll let you try to figure it out first.
Here's the whole thing.Note that prntSTR and Send_CRLF must be at the end of the program for the Director to work properly.Code:SER2_TX VAR PORTE.0 SER2_Baud CON 396 TRISB.6 = 1 ' Set USART TX pin to Output temp var byte Destination VAR BYTE @CRLF = 0x080 ; Reserve destination #8 for CR,LF after string '--[ Initialise USART for specified baud rate at current OSC speed ]------------ asm USART_Init macro Baud clrf TXSTA, 0 _SPBRG = (OSC * 1000000) / 16 / Baud - 1 ; calc SPBRG @ High baud rate if _SPBRG > 255 ; if SPBRG is too high _SPBRG = (OSC * 1000000) / 64 / Baud - 1 ; calc for Low baud rate bcf TXSTA, BRGH, 0 ; Set BRGH to Low Speed if _SPBRG > 255 _SPBRG = 255 endif else bsf TXSTA, BRGH, 0 ; Set BRGH to High Speed endif bsf TXSTA, TXEN, 0 ; Set Transmit Enable bit movlw _SPBRG movwf SPBRG, 0 ; load the calulated SPBRG movlw B'10010000' ; enable USART movwf RCSTA, 0 endm ENDASM ;----[Make_Destination]---- Compile Time Only - Does not use any Code space ---- ASM DestinationCount = 0 ; Global Make_Destination macro Nam, Sub Nam = 1 << DestinationCount ; Generate a unique ID Destination#v(DestinationCount) = Sub ; Save Subroutines Address DestinationCount += 1 endm ENDASM ;----[SendStr] ----------------------------------------------------------------- ASM SendStr macro Astring, Dest local TheStringData, BypassData goto BypassData ; Jump over the data TheStringData data Astring,0 ; Place String Data here BypassData MOVE?CB Dest, _Destination MOVE?CB low TheStringData, TBLPTRL ; Load the address of MOVE?CB low (TheStringData >> 8), TBLPTRH ; TheStringData into MOVE?CB low (TheStringData >> 16), TBLPTRU ; TABLPTR L?CALL prntSTR if (Dest && CRLF) > 0 L?CALL _Send_CRLF endif endm ENDASM ;----[ This macro will call each of the specified Destinations in sequence ]---- ASM Director macro local counter counter = 0 while counter < DestinationCount btfsc _Destination, counter CALL Destination#v(counter) counter += 1 endw endm ENDASM ;---[ These define the Destinations that will be called to output each byte ]--- ; There is currently a ; Name, _Subroutine MAXimum of 7 destinations @ Make_Destination HSER, _HSER_Send @ Make_Destination SER2, _SER2_Send @ Make_Destination LCD, _LCD_Send @ Make_Destination I2C, _I2C_Send Main: @ USART_Init 19200 @ SendStr "This is a string", HSER + CRLF @ SendStr "yet another string again", (HSER + SER2 + CRLF) @ SendStr "and one more time!", I2C @ SendStr "1 for the LCD", LCD Done: Goto Done ;------------------------------------------------------------------------------- HSER_Send: While PIR1.4 = 0 : Wend TXREG = temp return ;------------------------------------------------------------------------------- SER2_Send: SEROUT2 SER2_TX, SER2_Baud, [temp] Return ;------------------------------------------------------------------------------- LCD_Send: LCDOUT temp Return ;------------------------------------------------------------------------------- I2C_Send: ' However you need to do it here... RETURN ;----[prntStr]------------------------------------------------------------------ ASM ;--- TBLPTR should be pointing to 1st character in string before calling ------- prntSTR Next_Char tblrd *+ ; table read and post increment TBLPTR movff TABLAT,_temp ; fetch character from message string bra Test_EOM ; go test for EOM character Continue ; If not EOM then... Director ; Send the byte to each specified destination bra Next_Char ; fetch next message character from table Test_EOM movlw .0 ; check for EOM character cpfseq _temp,W ; compare temp with w, if temp = 0 then end bra Continue ; no EOM, so continue return ;finished with message, return to caller endasm ;---[Send Carriage Return, Line feed to the dest's specified in last SendStr]--- Send_CRLF: temp = 13 @ Director temp = 10 @ Director RETURN end
Well, have fun, and I hope this is closer to what you were looking for.
Best regards,
Darrel
Bookmarks