Several aspects of MIRTOS programming are demonstrated in this example. In most other examples, the MIRTOS system subroutine address definition files (i.e., one of Addresses328.Def, Addresses1284.Def, or Addresses2560.Def) were included, but they aren't here. Instead, the Indices.Def file is included. The constant indides which it contains are used in CALL or JUMP to SoftVector, which implements an indirect indirect jump into the System Subroutine table. This method works for all MCUs, so there is no difference in source code when accessing the MIRTOS system routines this way. It does take one more operation (specifically, initializing R30), and the indirect access changes R30, however.
The second thing shown is that, other than the two (2) used for contrast, six (6) of the FAR (or 4-BYTE) CALLs or JMPs to SoftVector have been replaced with NEAR (or 2-BYTE) RCALLs or RJMPs to SoftVector_VIn. Third, check out the two lines before SoftVector_VIn which are commented out. The placement of Display_Vin at the very end of the subroutine allows the JMP to replace the RCALL and immediate RETurn.
Finally, this routine can either be embedded within the Command Extensions code, or be assembled into a stand-alone block that can then be written to the MIRTOS-based device by itself.
/****************************************************************************** * * Subroutine "Display_Vin" * * Purpose: Obtain the Microcontroller input voltage, then format and print * its value to the serial port. * * Assumes: The MIRTOS Automatic A2D (Analog to Digital) conversion logic is * is enabled (BIT_SM_A2D in EEbSystemMode, set by "E 1 |= 1".) * The 9th Analog Input (index) is the one which has Vin * * Passed: Nothing. * * Returns: Nothing. * * Alters: R18 to R25, R30, and R31; also R26 if not a 328P MCU. * * Example: Call from a device's command console (while in Manager Mode): * "@ 0x3840" for 328P or "@ 0xF040" for 1284 or 2560 * * Notes: Try toggling the Register Dump mode using "}" and repeat. * ******************************************************************************/
There are some constant definition (.DEF) files which only need to be included if this source code is not being pulled into a the Command Extensions Assembly. There are other definitions which always need to be made. Notice how the nested .IF conditional assembly statements are indented and that the outer .ENDIF has a comment noting when it began. This is a way of making a note (perhaps only to yourself) where a specific conditional statment ends. Although this short example is fairly obvious, it's a good idea to use a comment on longer, nested conditional streams, particularly on those which span more than about one screen in your text editor.
.IFNDEF PROCESSOR ; You may want to explicitly specify an MCU PROCESSOR = 328 ; above, but if not ... .PRINT "Default 328P processor selected." .ENDIF .IFNDEF ACommandExtensions ; If assembling to stand alone, we need these: .INCLUDE "CommonDefs.Def" ; TRUE, FALSE, YES, NO, BIT_PV_*, et.al. .INCLUDE "BinDefs.Def" ; The B0 to B11111111 definitions .IF PROCESSOR == 328 .INCLUDE "IOM328P.Def" ; Register definitions for the ATmega328P .ELSE .IF PROCESSOR == 1284 .INCLUDE "IOM1284P.Def" ; Register definitions for the ATmega1284P .ELSE .INCLUDE "IOM2560.Def" ; Register definitions for the ATmega2560 ACSZ_Volts = 0x3A74C ; Address of the string "Volts" in 328P .ENDIF .ENDIF .INCLUDE "SystemEEPROM.Def" ; Names and addresses of system EEPROM variable .INCLUDE "SystemFLASH.Def" ; Names and addresses of system FLASH constants .INCLUDE "DisplayVin.Ptr" ; Pointers to labeled lines in this file .ENDIF ; of ".IFNDEF ACommandExtensions" .INCLUDE "Indices.Def" ; MCU independent MIRTOS system routine indices .IF PROCESSOR == 328 ACSZ_Volts = 0x3B3F ; Address of the string system "Volts" in 328P .ELSE .IF PROCESSOR == 1284 ACSZ_Volts = 0x1A94C ; System string "Volts" address in the 1284P .ELSE ACSZ_Volts = 0x3A74C ; System string "Volts" address in the 2560 .ENDIF .ENDIF
This next short block of source code simply defines some constants that you may need to change. In working with numerous MCUs, it was noticed that the Vin numerator constant may need to be tuned. Also, this subroutine's starting address can be changed here. This block was broken out in order to highlight where changes can be easily made and so it didn't get buried at the end of the longer block above.
UL_Vin_Numerator = 112640 ; Constant so Vin displays in #.## format BH_NUMERATOR = (UL_Vin_Numerator >> 24) ; = 0x1B800 (for 2 digits) B2_NUMERATOR = (UL_Vin_Numerator >> 16) & 0xFF B3_NUMERATOR = (UL_Vin_Numerator >> 8 ) & 0xFF ; This may need adjustment BL_NUMERATOR = UL_Vin_Numerator & 0xFF ; for your microcontroller SoftVector: ; SoftVector is always at address 0x0000 .IF PROCESSOR == 328 ; These are here to be able to start at .ORG 0x03840 ; the same location as they did in the .ELSE ; original MIRTOS-based device, but so .ORG 0x0F040 ; they also can be easily moved .ENDIF
Finally, with all of the housekeeping out of the way, this is the block which contains all of the source code that generates the subroutine image. Please note the system call to divide two ULONG (UNSIGNED LONG, i.e., 4-BYTE) values and another one to print a ULONG value ith implied decimal digits.
;------------ Display_Vin: ; Reminder: ASoftVector = 0 (is correct) LDI R24, 8 ; Specify which Analog Input index (which LDI R30, I_ReadAtoDValue ; is 0-based) so the MCU Vcc is index 8 CALL ASoftVector ; Retrieves the Analog Input into R25:R24 LDI R30, I_DumpRegisters ; Display all of the Registers (or not) CALL ASoftVector ; Method 1: use a FAR CALL to ASoftVector MOVW R18, R24 ; Place the ULONG divisor (or denominator) LDI R20, 0 ; into R21:R20:R19:R18 - this is the Vin LDI R21, 0 ; Analog Input just read in R25:R24 LDI R25, BH_NUMERATOR ; Load the dividend (or numerator) into LDI R24, B2_NUMERATOR ; R25:R24:R23:R22 - this is the CONSTANT LDI R23, B3_NUMERATOR ; value 112,604 LDI R22, BL_NUMERATOR LDI R30, I_DumpRegisters ; Comment out these 2 lines to omit the RCALL ASoftVector_VIn ; intermediate debugging output LDI R30, I_Divide2ULONGs ; Perform the ULONG division, which is: RCALL ASoftVector_VIn ; R25:R24:R23:R22 / R21:R20:R19:R18 LDI R30, I_DumpRegisters ; Quotient returned in R25:R24:R23:R22 RCALL ASoftVector_VIn ; Remainder returned in R21:R20:R19:R18 LDI R20, 2 ; Scale to 2 digits after the decimal, so LDI R30, I_PrintScaledULONG ; 502 will be output as 5.02, for example RCALL ASoftVector_VIn ; Method 2: use a NEAR CALL to one below .IF PROCESSOR != 328 LDI R26, ACSZ_Volts >> 16 .ENDIF LDI R25, (ACSZ_Volts >> 8) & 0xFF ; Specify the string "Volts" LDI R24, ACSZ_Volts & 0xFF Display_SpaceStringThenCRLF: MOVW R22, R24 ; Save the pointer to the "Volts" string LDI R30, I_PrintSpace ; Output one space RCALL ASoftVector_VIn MOVW R24, R22 ; Restore the "Volts string pointer LDI R30, I_PrintASCIIz ; Output the NULL-terminated string RCALL ASoftVector_VIn LDI R30, I_PrintNewLine ; Output a Carriage Return and Line Feed ;x RCALL ASoftVector_VIn ; No need to RCALL and RET; instead, just ;x RET ; fall through into the subroutine ... SoftVector_VIn: JMP ASoftVector ; This is a FAR (absolute address) JUMP .IFNDEF ASoftVector_VIn .PRINT "Assemble again to initialize label addresses." .ENDIF .BALIGN 16, 0xFF, 15
Copy the contents of each of the source code boxes above into the DisplayVin.Asm file. If assembling it as a stand-alone routine to be loaded into FLASH by itself, the DisplayVin.Ptr file will also need to be created (containing a single space, for example.) Be sure to assemble at least twice to pull in the constants updated into the .PTR file on each pass. Although not shown here, it's always a good idea to check the source listing to verify that no spelling or capitalization error caused an unexpected ".+0" in a branch instruction. Successful Assembly should look something like this for the 328P MCU, although typing the .TRM file is certainly optional. The six (6) rows of iHex shown below are the correct results of a successful Assembly of the source code blocks above for a 328P microcontroller.
C:\Testing>Asm DisplayVin Default 328P processor selected. Assemble again to initialize label addresses. C:\Testing>Asm DisplayVin Default 328P processor selected. C:\Testing>Type DisplayVin.Trm :1038400088E0EAE00E940000EAE70E9400009C0194 :1038500040E050E090E081E078EB60E0EAE710D0F3 :10386000E8E60ED0EAE70CD042E0E7E509D09BE3BA :103870008FE3BC01E9E504D0CB01E9E401D0E8E540 :103880000C940000FFFFFFFFFFFFFFFFFFFFFFFFA4 :00000001FF C:\Testing>
Of course, many changes could be made. For example, the resulting .iHex file has twelve (12) unused BYTEs which were padded with 0xFFs by the .BALIGN instruction. A local " Volts\r\n" string (with a leading space) could have been used instead of the call to print one space character, then the call to print the system "Volts" string, then print the Carriage Return and Line Feed characters. That would also remove the need to define ACSZ_Volts in each for the three MCUs. Why not try to make that change? Go for it!
Here are a couple even simpler examples. The three (3) calls to DumpRegisters could be removed. The two FAR CALLs to SoftVector at the very start could be replaced with NEAR RCALLs to the local SoftVector_VIn FAR JUMP. A slightly more complex change would be to INCLUDE each of the Addresses*.Def files and convert all of the indexed calls to absolute addresses. The idea here is to give you a starting point and provide a well-documented example.