⚡ Typical Power Profile of DUT #1 nRF52840 Sense. PPK2

:zap: SO here is a video of the power profiler from Nordic Semiconductor being used with a Xiao Device Under Test
this device is the Nrf52840 Sense, the video Depicts the power being consumed by the device while testing the flash and sleeping. code used is also included.

HTH
GL :slight_smile: PJ :v:

// 12/16/2023  Grove Expansion Board Flash Chip Test Code & OLED Display press button to SLEEP or Wake-UP
// from Adafruit_SPIFlash Example
// Author: PJG
//
//----------------------------------------------------------------------------------------------
// BSP : Seeed nRF52 Boards 1.1.1
// Board : Seeed XIAO nRF52840 Sense
// Grove Expansion Board & cheapest (I2C) SSD1306 display on AL-Gore Network & Grove push-button
//----------------------------------------------------------------------------------------------
// SdFat - Adafruit Fork@ 2.2.3         //    Pay close attention to the LIB versions.
// Adafruit SPIFlash@ 4.1.3             //     NOT all are compatable and New ones do NOT work
// Adafruit TinyUSB [email protected]       //   There are TOO MANY Flash libraries out. YMMV
//
// This tests and Identifies the Onboard Xiao QSPI Flash Chip
// and Expansion Flash Chip on Grove Board that is Hardwired to A1/D1 (pin 1) on Xiao
// for the Flash chipSelect CS or SS in the code
// Adafruit_SPIFlash is set with SPI class mthod. You can mix QSPI mode and SPI class are possible.
// The trick with the Nordic Nrf52x devices is the SPI interfaces as NRF_SPIM0, NRF_SPIM1, NRF_SPIM2,
// NRF_SPIM3 for which buss. device mode or method.
// HTH
//
#include <Arduino.h>
#include <SPI.h>
#include <Wire.h>
#include <SdFat.h>
#include <Adafruit_SPIFlash.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <LSM6DS3.h>
// Flash Chip declerations, made up from Data Sheets.
SPIFlash_Device_t const p25q16h{        
  // You can Declare the chip you choose this way or add it to the flash_devices
  // Configuration for W25Q80DV flash chip from the Data Sheet.
  .total_size = (1UL << 21),  // 2MiB
  .start_up_time_us = 10000,
  .manufacturer_id = 0x85,
  .memory_type = 0x60,
  .capacity = 0x15,
  .max_clock_speed_mhz = 55,
  .quad_enable_bit_mask = 0x02,
  .has_sector_protection = 1,
  .supports_fast_read = 1,
  .supports_qspi = 1,
  .supports_qspi_writes = 1,
  .write_status_register_split = 1,
  .single_status_byte = 0,
  .is_fram = 0,
};
SPIFlash_Device_t const w25q80{
  // Also seemsto be case sensative lower case preferred
  // Configuration for W25Q80DV flash chip from the Data Sheet.
  .total_size = (1UL << 20),  // 1MiB
  .start_up_time_us = 10000,
  .manufacturer_id = 0xEF,
  .memory_type = 0x40,
  .capacity = 0x14,
  .max_clock_speed_mhz = 55,
  .quad_enable_bit_mask = 0x02,
  .has_sector_protection = 0,
  .supports_fast_read = 1,
  .supports_qspi = 1,
  .supports_qspi_writes = 0,
  .write_status_register_split = 0,
  .single_status_byte = 0,
  .is_fram = 0,
};

//Create a instance of class LSM6DS3//#define int2Pin P0_11
LSM6DS3 myIMU(I2C_MODE, 0x6A);  //I2C device address 0x6A  // IMU
#define int2Pin PIN_LSM6DS3TR_C_INT1
#define OLED_RESET -1     //2 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 32  // OLED display height, in pixels
#define clkDuring 400000UL
#define clkAfter 100000UL

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET, clkDuring, clkAfter);

const int buttonPin = 2;     // the number of the pushbutton pin
int buttonState = 0;          // variable for reading the pushbutton status
uint8_t binterruptCount = 0;  // Amount of received interrupts
const int BuzzerPin = 1;
int buzzer = BuzzerPin;
#define LEDR LED_BUILTIN  // default
#define LEDG LED_GREEN
#define LEDB LED_BLUE
//#define LED LEDB
//#define LED P0_26   // RED
//#define LED P0_30   // GREEN
//#define LED P0_6    // BLUE
uint8_t interruptCount = 0;      // Amount of received interrupts
uint8_t prevInterruptCount = 0;  // Interrupt Counter from last loop
bool first10LoopCompleted = false;

#define SS_SPI0 1  // default to Pin 7 if you don't declare it as the CS for the Flash Chip A1/D1 pin1 on the Xiao
#define SS_SPI1 25  // Defaul SS or CS for the Onboard QSPI Flash Chip

SPIClass SPI_2(NRF_SPIM0, PIN_QSPI_IO1, PIN_QSPI_SCK, PIN_QSPI_IO0);  // Onboard QSPI Flash chip
Adafruit_FlashTransport_SPI QflashTransport(PIN_QSPI_CS, SPI_2);      // CS for QSPI Flash
Adafruit_SPIFlash Qflash(&QflashTransport);

SPIClass SPI_1(NRF_SPIM3, D9, D8, D10);                       // MISO, SCK, MOSI /EXPANSION FLASH// CS pin D1 (hardwired)
Adafruit_FlashTransport_SPI EflashTransport(SS_SPI0, SPI_1);  // Flash Type
Adafruit_SPIFlash Eflash(&EflashTransport);

// ******************** START *****************//
void setup() {
  Wire.begin();
  Serial.begin(9600);
  delay(2500);
  //while (!Serial) delay(2100);      // Code will wait until USB port is plugged in.
  pinMode(BuzzerPin, OUTPUT);
  buzzer = LOW ;  // Buzzer OFF
  Serial.println();
  Serial.println("Program " __FILE__ " compiled on " __DATE__ " at " __TIME__);
  Serial.println();
  Serial.println("Processor came out of reset.");
  Serial.println();
  pinMode(D6, INPUT_PULLUP);  // /WP
  //pinMode(D7, INPUT_PULLUP);  // /HOLD
  pinMode(SS_SPI0, OUTPUT);          // CS for flash
  digitalWrite(SS_SPI0, HIGH);       // <-- Set CS pin HIGH to deselect
  pinMode(buttonPin, INPUT_PULLDOWN);  // initialize the pushbutton pin as an input: for GROVE PB
                                     //attachInterrupt(digitalPinToInterrupt(buttonPin), myISR_Falling, FALLING);
  attachInterrupt(digitalPinToInterrupt(buttonPin), myISR_Rising, RISING);
  myIMU.settings.gyroEnabled = 0;  // Gyro currently not used, disabled to save power
  myIMU.begin();
  pinMode(int2Pin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(int2Pin), int1ISR, RISING);
  setupDoubleTapInterrupt();
  startsound();  //Beep Buzzer speaker on A3
  setupblink();  // Tests The onboard RGB LED
 Serial.println("--------------");
  Serial.println(SS_SPI0);
  Serial.println(MOSI); // master out, slave in
  Serial.println(MISO); // master in, slave out
  Serial.println(SCK);  // clock
 Serial.println("--------------");
  //startDisplay();            //Serial.println("SSD1306 allocation worked");
  Serial.println("Flash Testing Now.");
  Serial.println("Onboard QSPI Flash Testing");
  Serial.println(F("QSPI Flash ID P25Q16H"));
  //flashtestDisplay();
  // Initialize QSPI flash library and check its chip ID.
  if (!Qflash.begin(&p25q16h, 1)) {
    Serial.println(F("Error, failed to initialize QSPI flash chip!"));
   // while (1)
      ;
  }
  Serial.print(F("QFlash chip JEDEC ID: 0x"));
  Serial.println(Qflash.getJEDECID(), HEX);
  Serial.print(F("Flash size: "));
  Serial.print(Qflash.size() / 1024);
  Serial.println(F(" KB"));
  Serial.println(F("QFlash chip successfully ID'd!"));

  delay(1000);

  Serial.println();  // Keep Display the Same
  Serial.println("Expansion Flash testing!!");
  //digitalWrite(SS_SPI0, HIGH); // <-- Set CS pin HIGH to deselect
  Serial.println(F("SPI Flash ID W25Q80DV"));
  // Initialize W25Q80DV flash library and check its chip ID.
  if (!Eflash.begin(&w25q80,1)) {
    Serial.println(F("Error, failed to initialize W25Q80DV flash chip!"));
    while (1)
      ;
  }
  Serial.print(F("W25Q80DV chip JEDEC ID: 0x"));
  Serial.println(Eflash.getJEDECID(), HEX);
  Serial.print(F("Flash size: "));
  Serial.print(Eflash.size() / 1024);
  Serial.println(F(" KB"));
  Serial.println(F("SPI Flash chip successfully ID'd!"));
  delay(1000);
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // Address 0x3C for 128x32
    Serial.println("SSD1306 allocation failed");
  }
  delay(1000);
  startDisplay();
  flashtestDisplay();
  //delay (30000);

  //sd_power_system_off();
  //NRF_POWER->SYSTEMOFF = 1;
}
// **END of SETUP **//
void loop() {
  delay(250);
  digitalWrite(LED_BUILTIN, true);
  delay(500);
  digitalWrite(LED_BUILTIN, false);
  delay(500);
  if (first10LoopCompleted == true) {
    testTheflash();
  } else
    display.clearDisplay();
  display.setTextSize(1);       // Normal 1:1 pixel scale
  display.setTextColor(WHITE);  // Draw white text
  display.setCursor(0, 0);      // Start at top-left corner
  display.display();
  //display.clearDisplay();
  //display.display();// Your code here
  buttonState = digitalRead(buttonPin);  // read the state of the pushbutton value:
  if (buttonState == HIGH) {
    display.clearDisplay();
    display.setTextSize(1);       // Normal 1:1 pixel scale
    display.setTextColor(WHITE);  // Draw white text
    display.setCursor(0, 0);      // Start at top-left corner
    display.display();            // Go do it.
    display.println("  SLEEP  ");
    display.display();
    delay(2000);
    Serial.println("SLEEP/WAKE Button Pushed");
    delay(2000);
    menuMan();
    goToPowerOff();
  } else {
    buttonState = LOW;
    //menuMan();
  }
  delay(1500);
  buttonState = digitalRead(buttonPin);  // read the state of the pushbutton value:
  display.clearDisplay();
  display.setTextSize(2);       // Normal 1:1 pixel scale
  display.setTextColor(WHITE);  // Draw white text
  display.setCursor(10, 8);     // Start at top-left corner
  display.display();            // Go do it.
  display.println("  Idle.... ");
  display.display();
  delay(1000);
  display.setTextSize(2);       // Normal 1:1 pixel scale
  display.setTextColor(BLACK);  // Draw white text
  display.setCursor(10, 8);     // Start at top-left corner
  display.display();            // Go do it.
  display.println("  Idle.... ");
  display.display();
  delay(1000);
  display.setTextSize(1);       // Normal 1:1 pixel scale
  display.setTextColor(WHITE);  // Draw white text
  display.setCursor(10, 8);     // Start at top-left corner
  //display.display();
  display.println(" POWER SAVE ");
  display.display();
  delay(500);  // Go do it.
  Serial.println("SOFT DEVICE “SYSTEM ON” POWER SAVE");
  delay(1000);
  sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
  sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
  __WFE();
  __WFI();
  sd_app_evt_wait();  //SOFT DEVICE “SYSTEM ON” POWER SAVE

  delay(100);
}


//END//
void myISR_Falling() {  // Pressed Button Interrupt //
  binterruptCount++;
  buttonState = HIGH;
}
void myISR_Rising() {  // Pressed Button Interrupt //
  binterruptCount++;
  buttonState = HIGH;
}
//
// Functions *****************************************************
//
void startDisplay() {
  pinMode(LED_RED, OUTPUT);    // initialize the LED pin as an output:
  pinMode(LED_GREEN, OUTPUT);  // initialize the LED pin as an output:
  pinMode(LED_BLUE, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);  // initialize the LED pin as an output:
  display.clearDisplay();
  display.setTextSize(1);       // Normal 1:1 pixel scale
  display.setTextColor(WHITE);  // Draw white text
  display.setCursor(0, 0);      // Start at top-left corner
  display.display();            // Go do it.
  display.println("Display (StartUp)");
  display.display();
  delay(500);
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println("Power ON ");
  delay(1000);
  display.setCursor(0, 10);
  display.print("Flash Test:\n");  //Flash Test:0123456789
  display.display();
  delay(1500);
}
void flashtestDisplay() {
  display.setCursor(68, 10);
  display.print("--");
  display.display();
  delay(250);
  display.print("---");
  display.display();
  delay(250);
  display.print("---");
  display.display();
  delay(250);
  display.print("--");
  display.display();
  delay(500);
  first10LoopCompleted = true;
}
void menuMan() {
  display.setTextSize(1);       // Normal 1:1 pixel scale
  display.setTextColor(WHITE);  // Draw white text
  display.setCursor(0, 0);
  if (first10LoopCompleted = false) {
    display.println("Power ON ");
    display.display();
    delay(500);
  }
  display.setCursor(0, 10);
  display.print("Flash Test:\n");  //Flash Test:0123456789
  display.display();
  delay(100);
  display.clearDisplay();
  display.display();
  display.setCursor(12, 10);
  display.print("PASS:");
  display.print(Qflash.size() / 1024);
  display.println(F(" KB"));
  display.display();
  delay(500);
  display.setCursor(12, 18);
  display.print("PASS:");
  display.print(Eflash.size() / 1024);
  display.println(F(" KB"));
  display.display();
  delay(250);
  display.clearDisplay();
  display.display();
}
void testTheflash() {
  display.setTextColor(WHITE);  // Draw white text
  display.setTextSize(1);
  display.setCursor(4, 22);
  display.print("P25Q16H ");  // turn ON
  display.display();
  delay(500);
  display.setTextSize(2);
  display.setCursor(63, 18);
  display.print(" PASS");
  display.display();
  delay(500);
  display.setTextSize(1);
  display.setTextColor(BLACK);  // turn Off
  display.setCursor(4, 22);
  display.print("P25Q16H ");
  display.display();
  delay(500);
  display.setCursor(63, 18);
  display.setTextSize(2);
  display.print(" PASS");
  display.display();
  delay(250);
  display.setTextColor(BLACK);
  display.setCursor(63, 18);
  display.setTextSize(2);
  display.print(" PASS");
  display.display();
  delay(250);
  display.setCursor(4, 22);
  display.setTextSize(1);
  display.print("P25Q16H ");
  display.display();
  delay(500);
  display.setCursor(68, 10);
  display.setTextSize(1);
  display.print("**********");
  display.display();
  delay(500);
  display.setTextColor(WHITE);  // Draw white text
  display.setCursor(4, 22);
  display.print("W25Q80DV");
  display.display();
  delay(500);
  display.setCursor(68, 10);
  display.print("--");
  display.display();
  delay(500);
  display.print("---");
  display.display();
  delay(500);
  display.print("---");
  display.display();
  delay(500);
  display.print("--");
  display.display();
  delay(500);
  display.setCursor(63, 18);
  display.setTextSize(2);
  display.print(" PASS");
  display.display();
  delay(1000);
  display.setTextColor(BLACK);
  display.setCursor(4, 22);
  display.setTextSize(1);
  display.print("W25Q80DV");
  display.display();
  delay(500);
  first10LoopCompleted = false;
}
void setupblink() {

  setLedRGB(false, true, false);  // Red
  delay(1000);
  setLedRGB(true, false, false);  // Green
  delay(1000);
  setLedRGB(false, false, true);  // Blue
  delay(1000);
  setLedRGB(false, false, false);  // OFF
}

void setLedRGB(bool red, bool green, bool blue) {
  if (!blue) {
    digitalWrite(LEDB, HIGH);  } else {    digitalWrite(LEDB, LOW);  }
  if (!green) {
    digitalWrite(LEDG, HIGH);  } else {    digitalWrite(LEDG, LOW);  }
  if (!red) {
    digitalWrite(LEDR, HIGH);  } else {    digitalWrite(LEDR, LOW);  }
}
void startsound() {
  tone(buzzer, 890);
  delay(220);
  noTone(buzzer);
  delay(20);
  tone(buzzer, 800);
  delay(220);
  noTone(buzzer);
  delay(20);
  tone(buzzer, 800);
  delay(220);
  noTone(buzzer);
  delay(20);
  tone(buzzer, 990);
  delay(420);
  noTone(buzzer);
  delay(20);
}
void setupDoubleTapInterrupt() {

  uint8_t error = 0;
  uint8_t dataToWrite = 0;

  // Double Tap Config
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x60);     //* Acc = 416Hz (High-Performance mode)// Turn on the accelerometer ODR_XL = 416 Hz, FS_XL = 2g
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x8E);     // INTERRUPTS_ENABLE, SLOPE_FDS// Enable interrupts and tap detection on X, Y, Z-axis
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_THS_6D, 0x03);   // was 85 Set tap threshold 8C
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT_DUR2, 0x7F);     // Set Duration, Quiet and Shock time windows 7F
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x80);  // Single & double-tap enabled (SINGLE_DOUBLE_TAP = 1)
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x08);      // Double-tap interrupt driven to INT1 pin
}

void int1ISR() {
  interruptCount++;
  ;
}

void goToPowerOff() {
  //sleepFlash();
  setLedRGB(false, false, false);
  Serial.println("Going to SLEEP System OFF  Tap to WAKE \n");
  display.clearDisplay();
  display.setCursor(10, 8);
  display.setTextSize(3);
  display.print("SLEEP");
  display.display();
  delay(500);
  display.clearDisplay();
  display.display();
  setupDoubleTapInterrupt();  // not needed here, if already applied..
  delay(2000);                // delay seems important to apply settings, before going to System OFF
  //Ensure interrupt pin from IMU is set to wake up device
  // nrf_gpio_cfg_sense_input(digitalPinToInterrupt(int2Pin), NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
  delay(2000);  // delay seems important to apply settings, before going to System OFF
  uint32_t pin = D2;
  pin = g_ADigitalPinMap[pin];
  nrf_gpio_cfg_sense_input(pin, NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
  delay(2000);  //15 seconds before turns off (if USB is plugged in, that circuit will still draw 2mA)
  Serial.println(" You're NOT doing anything, I'll Sleep.");
  delay(2000);
  sd_power_system_off();
  NRF_POWER->SYSTEMOFF = 1;
}

and the 2nd test set is Chip with onboard Flash Only no other buttons or displays, this @msfujino code set.

//----------------------------------------------------------------------------------------------
// BSP : Seeed nRF52 Borads 1.1.8
// Board : Seeed nRF52 Borads / Seeed XIAO nRF52840 Sense 
//----------------------------------------------------------------------------------------------
// See datasheet "6.10.2 Port event"
// In order to prevent spurious interrupts from the PORT event while configuring the sources, the user
// shall first disable interrupts on the PORT event (through INTENCLR.PORT), then configure the sources
// (PIN_CNF[n].SENSE), clear any potential event that could have occurred during configuration
// (write '0' to EVENTS_PORT), and finally enable interrupts (through INTENSET.PORT).

// 2025/01/21

#include <Adafruit_SPIFlash.h>    // 5.0.0, NOTE:need to deleted /Documents/Arduino/libraries/SdFat
#include <flash_devices.h>
SPIFlash_Device_t const p25q16h {   // You can Declare the chip you choose this way or add it to the flash_devices
  // Configuration for p25q16h flash chip from the Data Sheet.
  .total_size = (1UL << 21), // 2MiB
  .start_up_time_us = 10000,
  .manufacturer_id = 0x85,
  .memory_type = 0x60,
  .capacity = 0x15,
  .max_clock_speed_mhz = 55,
  .quad_enable_bit_mask = 0x02,
  .has_sector_protection = 1,
  .supports_fast_read = 1,
  .supports_qspi = 1,
  .supports_qspi_writes = 1,
  .write_status_register_split = 1,
  .single_status_byte = 0,
  .is_fram = 0,
};
SPIFlash_Device_t const w25q80 {  // Also seemsto be case sensative lower case preferred
  // Configuration for W25Q80DV flash chip from the Data Sheet.
  .total_size = (1UL << 20), // 1MiB
  .start_up_time_us = 5000,
  .manufacturer_id = 0xEF,
  .memory_type = 0x40,
  .capacity = 0x14,
  .max_clock_speed_mhz = 104,
  .quad_enable_bit_mask = 0x02, 
  .has_sector_protection = 0,  
  .supports_fast_read = 1,     
  .supports_qspi = 1,           
  .supports_qspi_writes = 0,    
  .write_status_register_split = 0, 
  .single_status_byte = 0,     
  .is_fram = 0,                 
};

// for flashTransport definition
Adafruit_FlashTransport_QSPI flashTransport;
Adafruit_SPIFlash flash(&flashTransport);
static const SPIFlash_Device_t my_flash_devices[] = {p25q16h,};
const int flashDevices = 1;

#define PIN_WAKEUP (28)  // D0:(2), D1:(3), D2:(28), D3:(29), D4:(4), D5(5), D6(43), D7(44), D8(45), D9(46), D10(47)

void setup()
{
  // Enable DC-DC converter Mode
  NRF_POWER->DCDCEN = 1;            // Enable DC/DC converter for REG1 stage
  
  // on board Flash enter to Deep Power-Down Mode
  flash.begin(my_flash_devices, flashDevices);
  flashTransport.begin();
  flashTransport.runCommand(0xB9);  // enter deep power-down mode
  delayMicroseconds(5);             // tDP=3uS
  flashTransport.end();

  // LED on for 1 sec.
  digitalWrite(LED_GREEN, LOW);
  delay(1000);
  digitalWrite(LED_GREEN, HIGH);

// https://forum.seeedstudio.com/t/xiao-ble-long-boot-up-time/280116/13
//  NRF_POWER->GPREGRET = 0x6D; // Option : Set GPREGRET0 to 0x6D (DFU SKIP)

  NRF_GPIOTE->INTENCLR |= 0x80000000;   // disable interrupt for event PORT
//  nrf_gpio_cfg_sense_input(PIN_WAKEUP, NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);  // configure gpio for wakeup event
  nrf_gpio_cfg_sense_input(PIN_WAKEUP, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);  // configure gpio for wakeup event
  NRF_GPIOTE->EVENTS_PORT = 0;          // Event not generated
  NRF_GPIOTE->INTENSET |= 0x80000000;   // enable interrupt for event PORT

  // enter to Deep Sleep Mode
  NRF_POWER->SYSTEMOFF = 1;
}

void loop()
{
}



2 Likes

So here is the The “A+” version… Digikey employee delivers the goods on the Nordic PPK-II :+1:

Enjoy…

Awesome video! I’ve noticed you’d mentioned the external flash being installed for nrf52 Xiao board. Could you please give a hint, a link or anything on how to do this? I’ve done googling several days ago, but with not much luck.
Do I understand correctly, that with the internal flash and the external flash become a “single” memory, from that perspective, that this space is available for Program code, OTA and other partitions, as it was a single flash unit?

Hi there , Sure it not a state secret :smile:

HTH
GL :slight_smile: PJ :v:

Read through it, You be up to speed in NO time! :+1: