This section describes the message protocol used to communicate with the PIC and the message parser logic. This should be considered part or the application rather than the BRTOS although the lower level comms does have an interface to it in order to return error messages.
First an explanation of the protocol I have used. All messages comprise ASCII characters only, are variable in length and have a unique start of record character (“^”) and end of record character pair (CR,LF). I have used the CR, LF to terminate the record to make messages more readable when presented on the PDS Serial Communicator or other similar monitoring facilities.
The general format of the message is: ^CCC{,VAR,VAR,VAR}CRLF where CCC is the command string and the items within the curly brackets are optional depending on the message. The following messages are recognised by the demonstrator program:
GVN Requests the name and version number of the software.
Returns a string in the form:
RVN,“ProgName”, “VersionNo”
Where: ProgName is the name of the main Proton source file
VersionNo is the version number of the program
GTD Requests the Time and data.
Returns a string in the form:
RDT, “YYMMDD HHMMSS, D/S
Where: YYMMDD are Year, Month and Day as 2 decimal digits
HHMMSS are Hours, Minutes and Seconds as 2 decimal digits
D/S is a single binary digit representing the daylight saving state.
SDT, YYMMDDHHMMSS,D/S. Loads in a new Time and Date.
Where: YYMMDD are Year, Month and Day as 2 Hex digits
HHMMSS are Hours, Minutes and Seconds as 2 Hex digits
D/S as 2 decimal numbers representing the daylight saving state 0 or 1
If the message is accepted responds with AOK
XRS Returns a single Charater ("R") and enters a software reset
This function can be used to re-enter a bootloader program allowing remote reprogramming.
The remaining messages are used by the Task Monitor:
GTR Requests the task list start location for each task rate
Returns a string in the form:
RTR,“ImmdTask”,“2HzRate”,“1HzRate”, “1MinRate”, “1HourRate”,“COD”
Where: each parameter returned is expressed as a 2 digit decimal value.
GTA Requests list of all tasks in task list. Returns a string in the form:
RTA,“TaskRate”, “AlreadyThere”, “ListFull”, “Action”, “Address”
Where: TaskRate is a 2 digit decimal defining the position in the task list
AlreadyThere and List Full are 1 digit binary values
Address is a 4 digit hex representation of the Task address.
STK, N Requests changes to the monitoring of TASK_DEBUG.
Where N is a single decimal number which can have the following values:
0 Turns off all task reporting
1 Reports Task List Changes
2 Reports Current Active Task
3 Reports both 1 & 2 above
4 Reports Processor loading
5, 6, 7 As 1, 2 & 3 above plus Processor Loading report.
If the message accepted responds with AOK
Errors – If the message is not understood contains errors the response is ENN where: NN is a decimal number reporting the error code number.
There are 2 parts to message processing; detecting the presence of a valid record and processing the record. When we are running in full interrupt mode, once the record has been detected the code will drop directly into processing the record.
Detecting a Record
Once the ReceiveUSART Interrupt Service routine has received the minimum number of characters (in the case of the Demo 6 Chars) the Get_Record routine will be added to the Immediate Task and will be executed on return from Interrupt.
The first step is to check for errors. If a receive buffer overflow, Framing error or Overrun is detected or the first character in the receive buffer is not a start of record character an error will be reported back to the host and the buffer cleared.
If start of record has been found, the routine will check for an end of record character. Only the LF of the CR, LF terminating pair is checked. If we haven’t received a record terminator the routine will return and wait to be triggered by the next byte.
Once the LF character has been received the routine will stop the RxTMR and commence processing the record. If the RxTMR times out an Error message will be returned to the Host and the buffer cleared ready for a new message.
Process_Record:
This routine parses the record held in the buffer and generates a response.
The command is parsed in 2 parts; the first character is used to determine the type of command:
G – Get Commands have no parameters and are used request data.
S – Set Commands have parameters and are used to set up values or modes
X – Is a special case and is used as a dedicated reset command.
The next 2 characters of the command are processed as a pair, and are used to determine the action to be taken.
Parsing is implemented using nested Select statements, the first level testing the first character and the second level processing the 2 byte subcommand.
The code is pretty well commented so I will not go into line by line detail here. There are a few points worth noting however:
Error Codes – Where ever possible I have tried to provide useful error code responses, it helps hugely in debugging the communications. There is always a balance in that the more different error codes the greater the amount PIC code. It was for this reason that I decided not to use the modifiers available in Proton to convert ASCII into Hex values as they do not return with an error if the ASCII presented to them is outside 0-9 or A-F.
CRC Checks – Normal convention suggests one should provide cyclic redundancy checks into serial communications. Whilst this might well be advisable in some environments, I have opted not to here as I will be using the IP connection for my applications where the unit is remote. My RS232 connections are only used for development and program loading (until PDS can provide a bootloader which will support IP)
Conditional Assembly – When using conditional assembler directives within Proton Basic it is important that you only remove statements which do not affect the structure or context of the Basic code. This is demonstrated in the parsing of the Gxx command where I have #IFDEF TASK_DEBUG statements surrounding the code which allows the Visual Basic™ Task Monitor to report the activities of the program. Ideally one would place the conditional statements around whole case statement:
#IFDEF TASK_DEBUG
Case RE_TR
GoSub SendRateLocs ' Send Task Rate Locations
Case RE_TA
GoSub SendAllTasks ' Send all Tasks
#ENDIF
However, in doing this you are destroying the jump table that Proton has set up leading to disaster. Hence you must leave the case statements in and only comment out the actions.
Case RE_TR
#IFDEF TASK_DEBUG
GoSub SendRateLocs ' Send Task Rate Locations
#ENDIF
Case RE_TA
#IFDEF TASK_DEBUG
GoSub SendAllTasks ' Send all Tasks
#ENDIF
Comms Debugging – Debugging serial comms is always a difficult process made harder when you cannot see what is actually going on down the line. When debugging serial comms between a PC it is very useful to have a serial port monitor program to hand. I find the Advanced Serial Port Monitor from AGGSoftware very good for this purpose (http://aggsoft.com/serial-port-monitor/).
Picking up the Odds and Ends
This series of articles has covered all the main function of the BRTOS. There are 3 more Include files which make up the complete application which I will cover briefly for completeness.
Constants_P+.inc – This is a set of constants I use to aid readability – e.g. True = 1, False = 0 etc.
SWCBoardVer3.inc – These are the Defines and other bits and pieces to cover the hardware.
LCD_P+.inc – This is primarily a left over from PBPro days and originally hosted a whole suite of routines to help out with the display. All that remains now is a routine to control the contrast of the LCD and the CLL Macro which can be used to clear parts of the LCD. See below:
CLL {Line}, {Position}
With no parameters present CLL will clear the screen.
With Line parameter only will clear the line number in the parameter
With Line and Position parameters will clear to the end of the line from that position.
Quick Programming tip – Building up long in-line expressions can consume large amounts of additional code space, witness this example:
RTC_Data = (RTC_IntB >>4) * 10 + RTC_IntB & $0F
Consumes 44 bytes more than the equivalent code below
RTC_Data = RTC_IntB >> 4
RTC_Data = RTC_Data * 10
RTC_IntB = RTC_IntB & $0F
RTC_Data = RTC_Data + RTC_IntB