• AVR Freaks

Helpful ReplyHot!Reading ADRESH and ADRESL issues using PIC12

Author
DTsebrii
New Member
  • Total Posts : 9
  • Reward points : 0
  • Joined: 2021/03/01 13:31:48
  • Location: 0
  • Status: offline
2021/03/03 23:02:00 (permalink)
0

Reading ADRESH and ADRESL issues using PIC12

Hello everyone!
I decided to learn PIC Assembly using PIC12F1822. I wrote a program that supposes to read an ADC value, compare it with 512, and change the LED state according to the sampled value. After setting the ADGO bit (bit 1 in ADCON0), the microcontroller changes values inside ADRESH and ADRESL respectively to the potentiometer's value. However, when I am trying to save the value from these registers into WREG, the random values are stored there. 
Please, can you have a look at my code and say what I did wrong? I just started learning Assembly, thus my code is probably wrong. If you noticed something that I did wrong, I would be happy to hear about it. The source code is attached below. Thank you!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Name adcCheck.asm
; Author: DTsebrii
; Date: 02/28/2021
; Description: Program to set up a 10-bit ADC
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
; Pin 1 VDD (+5V) +5V
; Pin 2 RA5 LED_1 (Active high output)
; Pin 3 RA4 Pot
; Pin 4 RA3 MCLR
; Pin 5 RA2 N/O
; Pin 6 RA1/ICSPCLK
; Pin 7 RA0/ICSPDAT/AN0
; Pin 8 VSS (Ground) Ground
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
list p=12f1822,r=hex,w=0 ; list directive to define processor

nolist
include p12f1822.inc ; processor specific variable definitions
list
;;;; CONFIGURATION WORDS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
__CONFIG _CONFIG1,_FOSC_INTOSC & _WDTE_OFF & _MCLRE_ON & _IESO_OFF
; Internal oscillator, wdt is off, Pin4 is MCLR
__CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _LVP_OFF
; Constants
freqVal EQU b'01110000' ; 8MHz
initPort EQU b'00000000' ; PORTA all Voltage are low
TRISconf EQU b'11011111' ; All inputs except RA5
allDigit EQU b'00000000' ; RA3 is analog
chan3 EQU b'00001100' ; AN3 is ADC channel
adc1Conf EQU b'10010000' ; Right Just, Fosc/8 Vref = VDD
#define LED LATA, 5 ; LED pin
#define ADC_ON ADCON0, 0 ; TEnable ADC bit
#define AN3_AN ANSELA, 4 ; RA4 in analog
; Global Variables
cnt50us SET 33 ; A counter for 50us delay
ORG 0x00
; Main body
MainRoutine
CALL sysInit ; Function to initialize the system
loop1
CALL sampleADC
GOTO loop1

;;;; sysConfig ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Author: DTsebrii
;Date: 02/27/2021
;Description: Calling all subroutines required to
; configure the system
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
sysInit

CALL oscConfig
CALL portConfig
CALL adcConfig

RETURN
;;;; oscConfig ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Author: DTsebrii
;Date: 02/27/2021
;Description: Setting the oscillator frequency level and
; waiting until OSC is stable
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
oscConfig
BANKSEL OSCCON
MOVLW freqVal
MOVWF OSCCON
; Wait until OSC is stable
oscStable
BANKSEL OSCSTAT
BTFSS OSCSTAT, HFIOFS ; Check either HFIOFS is 1
GOTO oscStable

RETURN
;;;; portConfig ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Author: DTsebrii
;Date: 02/27/2021
;Description: Setting up the GPIO ports
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
portConfig
BANKSEL ANSELA
MOVLW allDigit
MOVWF ANSELA ; All pins are digital
BSF AN3_AN
BANKSEL LATA
MOVLW initPort
MOVWF LATA ; Output voltage is low

BANKSEL TRISA
MOVLW TRISconf
MOVWF TRISA ; RA5 is output

RETURN
;;;; adcConfig ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Author: DTsebrii
;Date: 02/28/2021
;Description: Setting up the ADC
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
adcConfig
BANKSEL ADCON1
MOVLW adc1Conf
MOVWF ADCON1

BANKSEL ADCON0
MOVLW chan3
IORWF ADCON0 ; Channel 3, GO is 0, ADC is OFF
BSF ADC_ON ; Turning on ADC

RETURN
;;;; sampleADC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Author: DTsebrii
;Date: 02/28/2021
;Description: Reading the ADC input and change the
; state of LED according to the ADC value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
sampleADC
;CALL delay50uS
BANKSEL ADCON0
BSF ADCON0, 1 ; Start sampling
busyADC
BTFSC ADCON0, 1 ; Wait till sampling is done
GOTO busyADC
; Check first 8 bit
BANKSEL ADRESL
MOVLW ADRESL
SUBWF 0x00 ; 512 = b'1000000000', so first 8 bit = 0
; Check rest
BANKSEL ADRESH
MOVLW ADRESH
SUBWFB 512 >> 8 ; Check top bits

BANKSEL STATUS
BTFSC STATUS, 0 ; Skip if ADRES > 512
GOTO turnOff
BANKSEL LATA
BSF LED
GOTO endADC
turnOff
BANKSEL LATA
BCF LED
endADC
RETURN
;;;; delay50us ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Author: DTsebrii
;Date: 02/21/2021
;Description: Taking a ucontroller under the loop
; for a 50uS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
delay50uS
; DECFSZ takes 1 cy in non-zero case, 2 cy otherwise
; GOTO takes 2 cy anyway
; loop will take 3 cy per time. If Fosc = 8MHz,
; Fcy = 2MHz => Tcy = .5us. To get 50us delay,
; 100 cy must be done. It will take 33 iterations
; Thus, cnt is equal to 33
waitLoop
DECFSZ cnt50us, 1
GOTO waitLoop

RETURN

END
 
#1
ric
Super Member
  • Total Posts : 30244
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/04 14:38:13 (permalink)
+2 (2)
"MOVLW ADRESL" is the wrong instruction.
That loads W with the address of the ADRESL register.
Change it to "MOVF ADRESL,W"
 

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#2
ric
Super Member
  • Total Posts : 30244
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/04 14:43:16 (permalink) ☄ Helpfulby DTsebrii 2021/03/05 10:01:10
+2 (2)
Also, "SUBWF 0x00" won't do what you want.
That subtracts the value of W from the register at address 0x00.
You want "SUBLW".
Have another read of the instruction set chapter, and pay particular attention to the difference between the "literal" instructions and those that access registers.
 

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#3
ric
Super Member
  • Total Posts : 30244
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/04 15:22:29 (permalink)
+2 (2)
This won't do what you expect.
cnt50us SET 33 ; A counter for 50us delay

and
DECFSZ cnt50us, 1

That will decrement a variable at address 33. It will NOT load that variable with the value of 33, so you will actually get 256 loops every time you call this function.
 

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#4
ric
Super Member
  • Total Posts : 30244
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/04 16:16:37 (permalink)
+2 (2)
and finally, here:
BANKSEL STATUS
BTFSC STATUS, 0 ; Skip if ADRES > 512

you never need to do a "BANKSEL STATUS". STATUS is a "core" register, which is available in all banks.
 
You are (correctly) including "p12F1822.inc". That gives you definitions for all the SFRs and bits in them.
You can make your code much more readable by using them.
BSF ADCON0, 1 ; Start sampling
can be
BSF ADCON0, GO ; Start sampling
 
BTFSC STATUS, 0
can be
BTFSC STATUS, C
 
DECFSZ cnt50us, 1
can be
DECFSZ cnt50us, F

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#5
Murton Pike Systems
Super Member
  • Total Posts : 247
  • Reward points : 0
  • Joined: 2020/09/10 02:13:01
  • Location: 0
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/04 19:50:06 (permalink)
+2 (2)
I would recommend getting into C.
I have hated subwf from the first day I worked on PIC's in 1985.
I was used to Z80  compare instruction where nc is >= and c is less than.
PIC is different.
 
In C your adresl/h can be put in a 16 bit variable which is easier to work with.
Then you can do a compare for 512 easily.
 
 
#6
1and0
Access is Denied
  • Total Posts : 12269
  • Reward points : 0
  • Joined: 2007/05/06 12:03:20
  • Location: Harry's Gray Matter
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/04 20:53:38 (permalink)
+1 (1)
nigelwright7558
I would recommend getting into C.

I would recommend learning assembly. It will help when you move to C.
 
nigelwright7558
I have hated subwf from the first day I worked on PIC's in 1985.
I was used to Z80  compare instruction where nc is >= and c is less than.
PIC is different.

That is because subtraction in PIC is actually executed by adding the 2's complement of the second operand.
 
nigelwright7558
In C your adresl/h can be put in a 16 bit variable which is easier to work with.
Then you can do a compare for 512 easily.

A 16-bit variable is just two 8 bits. In assembly, compare for >512 can be easily worked as 8 bits instead of 16 bits. ;)
 
 
post edited by 1and0 - 2021/03/04 21:06:45
#7
ric
Super Member
  • Total Posts : 30244
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/04 20:55:05 (permalink)
+2 (2)
nigelwright7558
I would recommend getting into C.

+1.
e.g.
// PIC12F1822 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = HI        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), high trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#define _XTAL_FREQ 8000000  //tell compiler we will be running at 8MHz

//Define some constants used in the code
#define FREQVAL 0b01110000  // 8 MHz
#define ALLDIGIT 0b00000000 // RA3 is analog (this comment doesn't agree with this value)
#define INITPORT 0b00000000 // PORTA all Voltage are low
#define TRISCONF 0b11011111 // All inputs except RA5
#define ADC1CONF 0b10010000 // Right Just, Fosc/8 Vref = VDD
#define CHAN3 0b00001100    // AN3 is ADC channel
#define LED LATAbits.LATA5  // LED on RA5

void oscConfig(void)
{
    OSCCON = FREQVAL;
    while (OSCSTATbits.HFIOFS == 0)   //wait until oscillator is stable
        ;
}

void portConfig(void)
{
    ANSELA = ALLDIGIT;
    ANSELAbits.ANSA4 = 1;    //make just AN3 (RA4) analog (it would be better just to do that when you initialise ANSEL)
    LATA = INITPORT;
    TRISA = TRISCONF;
}

void adcConfig(void)
{
    ADCON1 = ADC1CONF;
    ADCON0 |= CHAN3; //select channel 3, leave ADC off ("ADCON0bits.CHS = 3;" works in C)
    ADCON0bits.ADON = 1;    //
}

void sampleAdc(void)
{
    __delay_us(50);  //delay 50us
    ADCON0bits.GO = 1;  //start converting
    while (ADCON0bits.GO == 1)  //wait until conversion finished
        ;
    
    if (ADRES < 512)
        LED = 0;    //turn LED off
    else
        LED = 1;    //turn LED on
}

void main(void) {
    oscConfig();
    portConfig();
    adcConfig();
    while (1)   //loop forever
    {
        sampleAdc();
    }
}




I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#8
1and0
Access is Denied
  • Total Posts : 12269
  • Reward points : 0
  • Joined: 2007/05/06 12:03:20
  • Location: Harry's Gray Matter
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/05 04:02:09 (permalink) ☄ Helpfulby DTsebrii 2021/03/05 10:05:15
+1 (1)
DTsebrii
I decided to learn PIC Assembly using PIC12F1822. I wrote a program that supposes to read an ADC value, compare it with 512, and change the LED state according to the sampled value.

The long form of comparing against 512 is this
        movlw   low (.512)
        subwf   ADRESL,w
        movlw   high(.512)
        subwfb  ADRESH,w

and then test the Carry bit for '0' when ADRES < 512 or '1' when ADRES >= 512.
 
Or, you can simply ignore the lower byte ADRESL and instead replace this
sampleADC
;CALL delay50uS
BANKSEL ADCON0
BSF ADCON0, 1 ; Start sampling
busyADC
BTFSC ADCON0, 1 ; Wait till sampling is done
GOTO busyADC
; Check first 8 bit
BANKSEL ADRESL
MOVLW ADRESL
SUBWF 0x00 ; 512 = b'1000000000', so first 8 bit = 0
; Check rest
BANKSEL ADRESH
MOVLW ADRESH
SUBWFB 512 >> 8 ; Check top bits

BANKSEL STATUS
BTFSC STATUS, 0 ; Skip if ADRES > 512
GOTO turnOff
BANKSEL LATA
BSF LED
GOTO endADC
turnOff
BANKSEL LATA
BCF LED
endADC
RETURN

with this
sampleADC
        call    delay50uS   ; delay 50us
        banksel ADCON0      ;
        bsf     ADCON0,GO   ; start converting
        btfsc   ADCON0,GO   ; wait until conversion finished
        bra     $-1         ;
        
        lsrf    ADRESH      ; if (ADRES < 512)
        banksel LATA        ;
        skpnz               ;
        bcf     LED         ;   turn LED off
        skpz                ; else
        bsf     LED         ;   turn LED on
        return              ;

 
And replace this
delay50uS
; DECFSZ takes 1 cy in non-zero case, 2 cy otherwise
; GOTO takes 2 cy anyway
; loop will take 3 cy per time. If Fosc = 8MHz,
; Fcy = 2MHz => Tcy = .5us. To get 50us delay,
; 100 cy must be done. It will take 33 iterations
; Thus, cnt is equal to 33
waitLoop
DECFSZ cnt50us, 1
GOTO waitLoop
RETURN

with this
delay50uS
        movlw   .32         ; 50 us @ Fosc = 8 MHz -> 100 Tcy
        decfsz  WREG        ; Tcy = 2(CALL) + (3*WREG) + 2(RETURN)
        bra     $-1
        return

 
DTsebrii
list p=12f1822,r=hex,w=0 ; list directive to define processor

I would recommend using radix decimal, assuming you have only 10 fingers. ;)
 
#9
DTsebrii
New Member
  • Total Posts : 9
  • Reward points : 0
  • Joined: 2021/03/01 13:31:48
  • Location: 0
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/05 10:04:41 (permalink)
0
ric
Have another read of the instruction set chapter, and pay particular attention to the difference between the "literal" instructions and those that access registers.
 

Thank you for all your suggestions! Now it works. I definitely will have another look at the instructions set, that is just how I learn. I need to try first, and then I will be able to understand the subject.  
#10
DTsebrii
New Member
  • Total Posts : 9
  • Reward points : 0
  • Joined: 2021/03/01 13:31:48
  • Location: 0
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/05 10:07:49 (permalink)
0
1and0
DTsebrii
I decided to learn PIC Assembly using PIC12F1822. I wrote a program that supposes to read an ADC value, compare it with 512, and change the LED state according to the sampled value.

The long form of comparing against 512 is this
        movlw   low (.512)
        subwf   ADRESL,w
        movlw   high(.512)
        subwfb  ADRESH,w

and then test the Carry bit for '0' when ADRES < 512 or '1' when ADRES >= 512.
 
Or, you can simply ignore the lower byte ADRESL and instead replace this
sampleADC
;CALL delay50uS
BANKSEL ADCON0
BSF ADCON0, 1 ; Start sampling
busyADC
BTFSC ADCON0, 1 ; Wait till sampling is done
GOTO busyADC
; Check first 8 bit
BANKSEL ADRESL
MOVLW ADRESL
SUBWF 0x00 ; 512 = b'1000000000', so first 8 bit = 0
; Check rest
BANKSEL ADRESH
MOVLW ADRESH
SUBWFB 512 >> 8 ; Check top bits

BANKSEL STATUS
BTFSC STATUS, 0 ; Skip if ADRES > 512
GOTO turnOff
BANKSEL LATA
BSF LED
GOTO endADC
turnOff
BANKSEL LATA
BCF LED
endADC
RETURN
 

with this
sampleADC
        call    delay50uS   ; delay 50us
        banksel ADCON0      ;
        bsf     ADCON0,GO   ; start converting
        btfsc   ADCON0,GO   ; wait until conversion finished
        bra     $-1         ;
        
        lsrf    ADRESH      ; if (ADRES < 512)
        banksel LATA        ;
        skpnz               ;
        bcf     LED         ;   turn LED off
        skpz                ; else
        bsf     LED         ;   turn LED on
        return              ;

 
And replace this
delay50uS
; DECFSZ takes 1 cy in non-zero case, 2 cy otherwise
; GOTO takes 2 cy anyway
; loop will take 3 cy per time. If Fosc = 8MHz,
; Fcy = 2MHz => Tcy = .5us. To get 50us delay,
; 100 cy must be done. It will take 33 iterations
; Thus, cnt is equal to 33
 
waitLoop
DECFSZ cnt50us, 1
GOTO waitLoop
RETURN
 

with this
delay50uS
        movlw   .32         ; 50 us @ Fosc = 8 MHz -> 100 Tcy
        decfsz  WREG        ; Tcy = 2(CALL) + (3*WREG) + 2(RETURN)
        bra     $-1
        return

 
DTsebrii
list p=12f1822,r=hex,w=0 ; list directive to define processor

I would recommend using radix decimal, assuming you have only 10 fingers. ;)
 


Thank you for your help. Now my code works, but I think, it still is not structured enough. Besides learning the instruction set, I still need to familiarize myself with the structured coding practices in Assembly. 
#11
DTsebrii
New Member
  • Total Posts : 9
  • Reward points : 0
  • Joined: 2021/03/01 13:31:48
  • Location: 0
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/05 10:12:50 (permalink)
0
nigelwright7558
I would recommend getting into C.
 

Thank you for the suggestion! I started learning PIC with C. Now I wanted to have a deeper understanding of what is really going on inside of a microcontroller. Additionally, I can work with my PIC12 only with MPASM. I did not google either I can use C with it or not (I do not have access to the CSS compiler). Instead, I decided this is a good opportunity for me to learn something new. 
#12
1and0
Access is Denied
  • Total Posts : 12269
  • Reward points : 0
  • Joined: 2007/05/06 12:03:20
  • Location: Harry's Gray Matter
  • Status: offline
Re: Reading ADRESH and ADRESL issues using PIC12 2021/03/05 12:36:41 (permalink)
+1 (1)
DTsebrii
Additionally, I can work with my PIC12 only with MPASM. I did not google either I can use C with it or not (I do not have access to the CSS compiler). Instead, I decided this is a good opportunity for me to learn something new. 

Microchip XC8 C compiler works with all 8-bit PIC10/12/16/18 devices.
#13
Jump to:
© 2021 APG vNext Commercial Version 4.5