S32K144 Peripherals

UART Peripheral in S32K144 MCU

So hello guys, welcome back to NXP Semiconductors S32K144 MCU Tutorial series. In the last blog we had started with S32K144 MCU GPIO Peripheral. In this blog we are going to explore the UART Peripheral. Going to Start with UART peripheral. Objective would be to get.

  • familiarity with UART peripheral for S32K144 MCU.
  • Would be understanding UART peripheral from Hardware point of view in S32K144 MCU.
  • Going to understand then how to use UART peripheral via S32K SDK/uart driver.
  • Would also be demonstrating the uart_echo_pall sketch.

So read along the blog and do tell me its reviews!

Table of Contents

UART Peripheral Theory

UART is a serial communication protocol which is used to interface external sensor & IoT modules to the Host MCU. IoT modules like: GSM Module, GPS Module, BLE Modules, WiFi Modules are connected to Host MCU via UART protocol. Thus, understanding of UART communication protocol and its driver implementation, plays a crucial role.

To know about UART peripheral in theory, viewers can refer to this blog.

LPUART Peripheral in S32K144 MCU

In S32K144 MCU, UART can be used via 2 peripherals: LPUART & FlexIO.

UART in S32K144 MCU

LPUART is referred as Low Power Universal Asynchronous Receiver/Transmitter. LPUART is onchip peripheral only to do UART communication protocol. UART is a serial communication protocol which is done via UART peripheral in the Microcontrollers.

Also, in S32K144, there is FlexIO peripheral through which on-board serial communication protocols like UART, I2C & SPI can be emulated. So, through FlexIO peripheral, also UART peripheral can be implemented. To know about FlexIO peripheral in S32K144, refer to this blog.

Features of UART via LPUART peripheral in S32K144 MCU:

  • LPUART peripheral of S32K144 supports full duplex mode and has NRZ data encoding (That is Logic 1 bit represents High value and Logic 0 bit represents Low value).
  • It has programmable Baud rates(
  • LPUART peripheral supports Interrupts mode, DMA mode or pooled operation mode for transmitting/receiving the data using LPUART protocol or for detecting below listed errors while LPUART communication session is happening:
  1. Transmit data register empty and transmission complete.
  2. Receive data register full.
  3. Receive overrun, parity error, framing error, and noise error.
  4. Idle receivers detect.
  5. Active edge on receive pin.
  6. Break detect supporting LIN.
  7. Receive data match.
  • S32K144 also supports hardware parity generation and checking to ensure the integrity of the data while LPUART communication is happening.
  • It has Programmable LPUART data length of 7-10 bits and LPUART stop bits of 1-2 bits.
  • S32K144 MCU has feature to use the LPUART peripheral to wake up the MCU (if it is programmed in sleep modes), it can be waked up in either of the 3 below mentioned events:
  1. Idle line wakeup
  2. Address mark wakeup
  3. Receive data match.
  • Support of request to send (RTS) and clear to send (CTS) signals, for hardware flow control,  example of hardware flow control is a half-duplex radio modem to computer interface.
  • Support of IrDA 1.4 format with programmable pulse width for IR communication.
  • LPUART peripheral of S32K144 supports independent FIFO buffer for transmission and receiving of Data.
  • Also, LPUART peripheral FIFO of S32K144 has additional features like:
  1. configurable watermark (lowest number of bytes) for transmit and receive buffer, when watermark is achieved corresponding status is changed of LPUART peripheral.
  2. Option for receiver to assert request after a configurable number of idle. 
    characters if receive FIFO is not empty.
  • Tx and Rx FIFO in S32K144 for all LPUART Instances both are of 4 words size. 1 Word is 1 byte(8 bits). So FiFo size is 4 byte long(32 bits).

LPUART Hardware Pinout in S32K144 MCU

LPUART Pinout and Hardware Instances

LPUART peripheral in S32K144 has 3 instances: LPUART0, LPUART1, LPUART2.

All the LPUART Instances has 4 pins: 

  • RX: Receiving of data
  • TX: Transmission of data
  • CTS: Clear to Send pin for hardware flow control.
  • RTS: Request to Send for hardware flow control.
    S32K144 UART Pins

Each LPUART instance supports all the 4 pins, with below mentioned pin details.

How to use LPUART Pins in S32K144 MCU for doing UART Communication?

Now for doing UART communication, we need to connect hardware UART pins of the MCU, for doing so follow the steps:

  1. At first select which instance of LPUART wanna use, once selected. As told above their are 3 instances of LPUART in S32K144 MCU.
  2. Once selected, then use above excel to short select which pin of corresponding LPUART has to be used. 
  3. Lets say we use LPUART1 and use PTC7 and PTC6 as Tx & Rx pins of LPUART.
  4. So connect these pins of MCU with external UART Module.
  5. Remember Tx pin of MCU is connected to Rx pin of external UART Module and Rx pin of MCU is connected to Tx pin of external UART Module.
  6. Here i am going to use USB to serial FTDI connector with Original S32K144 evaluation2 board.
  7. With ElecronicsV2 board, it has on-board UART driver IC at LPUART1 PTC7 and PTC6 pins.

How to configure UART Peripheral in S32K144 using S32 Design Studio IDE

UART Peripheral of S32K144 MCU can be configured using the Peripheral configuration tools of S32 Design Studio. These tools provide us with the GUI interface to configure the UART Peripheral of the MCU. To know in detail about the Peripheral configuration of S32 Design Studio refer to these.

BLE Module interfacing with S32K144 MCU
GPS Module interfacing with S32K144 MCU
WiFi Module interfacing with S32K144
GSM Module interfacing with S32K144

LPUART SDK for S32K144 MCU

LPUART SDK

S32K SDK/drivers provide an easy to use and quick way to use LPUART peripheral in S32K144, which is known as LPUART SDK.

LPUART SDK for S32K MCU’s

Each S32 SDK driver can be configured and enabled to use in the project via S32 Configuration Tool. Will be digging into that part, in next section. For now, let’s understand the LPUART SDK in some detail, so as to use UART peripheral via LPUART.

LPUART SDK files

In the SDK of LPUART there are header files and source files for LPUART Driver and LPUART Interrupt.

  • LPUART interrupt files contains functions for using &configuring of LPUART interrupts and IRQ handler in S32K144 MCU.
  • LPUART driver files contains functions for using/configuration of LPUART Peripheral.

LPUART Driver 

LPUART driver files are further divided into LPUART Peripheral Abstraction Layer (PAL) & LPUART Low-level drivers., as shown below:

LPUART Driver Files
  • LPUART Peripheral Abstraction Layer(PAL): contains functions and variables that are directly used in main.c or application code. And internally these functions use the LPUART Low-level drivers & LPUART IRQ . So if hardware is changed LPUART PAL would remain same and only internal low-level driver files need to be changed or modified. By this way we don’t have make many changes on application level.
  • LPUART Low-level driver: contains functions that configures the LPUART peripheral registers for initializing the peripheral, using the peripheral and processing the data of peripheral at hardware level. These files are the ones which actually interacts with the hardware and make it configurable to our needs. 

In the blogs we will be exploring the LPUART Peripheral Abstraction Layer files (PAL) in more details, as that would be directly used in our application project development(main.c) and driver creation of UART modules (GPS, GSM, IoT Modules and etc)

LPUART PAL

In LPUART PAL there are 2 files lpuart_driver.c and lpuart_driver.h files.

Lets get into these files:

  1. lpuart_driver.h: contains the enums, structures and function declarations that would be used in application code. Only functions which are declared in this header file can be used in main.c or application project.
  2. lpuart_driver.c: contains the function definations of the declared functions(uses the low-level driver functions) along with some static functions also that are restricted to use in this file only.

Functions

How to use LPUART PAL for doing UART Communication in S32K144 MCU.

  1. Features of LPUART PAL.
  2. Initialization and configuration API’s of UART peripheral using LPUART PAL.
  • LPUART_DRV_Init: This function is the first function that has to be used in main.c to initialize the LPUART peripheral. This function is just like Serial.begin(9600) of Arduino IDE environment.
				
					status_t LPUART_DRV_Init(uint32_t instance, lpuart_state_t * lpuartStatePtr,
                         const lpuart_user_config_t * lpuartUserConfig);

				
			

This function has 3 function parametrs as follows:

  1. instance: integer number indicating which instance of LPUART we are going to use.
  2. lpuart_user_config and lpuart_state_t: structure pointers have to be sent in this function as parameters. (For the info on these structures refers next section)
				
					#define INST_LPUART_LPUART_1  1

extern lpuart_state_t lpUartState1;

/* External declaration of LPUART configuration structure */
extern const lpuart_user_config_t lpUartInitConfig1;

lpuart_state_t lpUartState1;

const lpuart_user_config_t lpUartInitConfig1 = {
  .transferType = LPUART_USING_INTERRUPTS,
  .baudRate = 9600UL,
  .parityMode = LPUART_PARITY_DISABLED,
  .stopBitCount = LPUART_ONE_STOP_BIT,
  .bitCountPerChar = LPUART_8_BITS_PER_CHAR,
  .rxDMAChannel = 0UL,
  .txDMAChannel = 0UL
};

 /* Initialize LPUART instance */
  LPUART_DRV_Init(INST_LPUART_LPUART_1, &lpUartState1, &lpUartInitConfig1);
 
				
			

In LPUART PAL their are 3 ways by which we can send and receive data.

  1. Interrupt based
  2. DMA Based 
  3. Pooling Based.

In this blog, we are going to explore and understand pooling based method and it’s API. As to use by interrupt and DMA based, we have to know about Interrupt and DMA. So we are going to cover Interrupt and DMA based LPUART communication under topics: Interrupts in S32K144 and DMA in S32K144 MCU.

What is pooling method in Embedded?

LPUART PAL API’s for doing pooling based communication

  • LPUART_DRV_SendDataPolling
				
					status_t LPUART_DRV_SendDataPolling(uint32_t instance, const uint8_t *txBuff, uint32_t txSize);

				
			

This function has 3 parametrs:

  1. instance: integer number indicating which instance of LPUART we are going to use.
  2. txBuff: buffer pointer, pointing to the data which needs to be send out.
  3. txSize: size of data that has to be sent.
				
					  /* Welcome message displayed at the console */
#define welcomeMsg "This example is an simple echo using LPUART\r\n\
it will send back any character you send to it.\r\n\
The board will greet you if you send 'Hello Board'\r\
\nNow you can begin typing:\r\n"

  
  /* Send a welcome message */
  LPUART_DRV_SendDataPolling(INST_LPUART_LPUART_1, (uint8_t *)welcomeMsg, strlen(welcomeMsg));

				
			
  • LPUART_DRV_ReceiveDataPolling
				
					status_t LPUART_DRV_ReceiveDataPolling(uint32_t instance, uint8_t *rxBuff, uint32_t rxSize);

				
			

This function has 3 function parameters:

  1. instance: integer number indicating which instance of LPUART we are going to use.
  2. rxBuff: buffer pointer, would be containing the received data.
  3. rxSize: size of data that has to be received.
				
					  /* Receive buffer size */
#define BUFFER_SIZE 256U

/* Buffer used to receive data from the console */
uint8_t buffer[BUFFER_SIZE];

LPUART_DRV_ReceiveDataPolling(INST_LPUART_LPUART_1, buffer, 1U);
    
				
			
Data Types

There are 2 structures that are important and will be used often

  • lpuart_user_config_t: This structure has members to configure the LPUART according to user defined settings.
				
					/*! @brief LPUART configuration structure
 *
 * Implements : lpuart_user_config_t_Class
 */
typedef struct
{
    uint32_t baudRate;                           /*!< LPUART baud rate */
    lpuart_parity_mode_t parityMode;             /*!< parity mode, disabled (default), even, odd */
    lpuart_stop_bit_count_t stopBitCount;        /*!< number of stop bits, 1 stop bit (default) or 2 stop bits */
    lpuart_bit_count_per_char_t bitCountPerChar; /*!< number of bits in a character (8-default, 9 or 10);
                                                      for 9/10 bits chars, users must provide appropriate buffers
                                                      to the send/receive functions (bits 8/9 in subsequent bytes);
                                                      for DMA transmission only 8-bit char is supported. */
    lpuart_transfer_type_t transferType;         /*!< Type of LPUART transfer (interrupt/dma based) */
    uint8_t rxDMAChannel;                        /*!< Channel number for DMA rx channel.
                                                      If DMA mode isn't used this field will be ignored. */
    uint8_t txDMAChannel;                        /*!< Channel number for DMA tx channel.
                                                      If DMA mode isn't used this field will be ignored. */
} lpuart_user_config_t;


				
			
				
					/* External declaration of LPUART configuration structure */
extern const lpuart_user_config_t lpUartInitConfig1;

const lpuart_user_config_t lpUartInitConfig1 = {
  .transferType = LPUART_USING_INTERRUPTS,
  .baudRate = 9600UL,
  .parityMode = LPUART_PARITY_DISABLED,
  .stopBitCount = LPUART_ONE_STOP_BIT,
  .bitCountPerChar = LPUART_8_BITS_PER_CHAR,
  .rxDMAChannel = 0UL,
  .txDMAChannel = 0UL
};
				
			
  • lpuart_state_t: This structure has data members, which keep track of the on-going transfers . 
				
					/*!
 * @brief Runtime state of the LPUART driver.
 *
 * Note that the caller provides memory for the driver state structures during
 * initialization because the driver does not statically allocate memory.
 *
 * Implements : lpuart_state_t_Class
 */
typedef struct
{
    const uint8_t * txBuff;              /*!< The buffer of data being sent.*/
    uint8_t * rxBuff;                    /*!< The buffer of received data.*/
    volatile uint32_t txSize;            /*!< The remaining number of bytes to be transmitted. */
    volatile uint32_t rxSize;            /*!< The remaining number of bytes to be received. */
    volatile bool isTxBusy;              /*!< True if there is an active transmit.*/
    volatile bool isRxBusy;              /*!< True if there is an active receive.*/
    volatile bool isTxBlocking;          /*!< True if transmit is blocking transaction. */
    volatile bool isRxBlocking;          /*!< True if receive is blocking transaction. */
    lpuart_bit_count_per_char_t bitCountPerChar; /*!< number of bits in a char (8/9/10) */
    uart_callback_t rxCallback;          /*!< Callback to invoke for data receive
                                              Note: when the transmission is interrupt based, the callback
                                              is being called upon receiving a byte; when DMA transmission
                                              is used, the bytes are copied to the rx buffer by the DMA engine
                                              and the callback is called when all the bytes have been transferred. */
    void * rxCallbackParam;              /*!< Receive callback parameter pointer.*/
    uart_callback_t txCallback;          /*!< Callback to invoke for data send
                                              Note: when the transmission is interrupt based, the callback
                                              is being called upon sending a byte; when DMA transmission
                                              is used, the bytes are copied to the tx buffer by the DMA engine
                                              and the callback is called when all the bytes have been transferred. */
    void * txCallbackParam;              /*!< Transmit callback parameter pointer.*/
    lpuart_transfer_type_t transferType; /*!< Type of LPUART transfer (interrupt/dma based) */
#if FEATURE_LPUART_HAS_DMA_ENABLE
    uint8_t rxDMAChannel;                /*!< DMA channel number for DMA-based rx. */
    uint8_t txDMAChannel;                /*!< DMA channel number for DMA-based tx. */
#endif
    semaphore_t rxComplete;              /*!< Synchronization object for blocking Rx timeout condition */
    semaphore_t txComplete;              /*!< Synchronization object for blocking Tx timeout condition */
    volatile status_t transmitStatus;    /*!< Status of last driver transmit operation */
    volatile status_t receiveStatus;     /*!< Status of last driver receive operation */
} lpuart_state_t;

				
			
				
					extern lpuart_state_t lpUartState1;

lpuart_state_t lpUartState1;

				
			

UART Demo Code for S32K144 MCU

 S32 SDK/provides as example demo on LPUART driver to demonstarate how to use the LPUART driver for UART communicatio protocol. Demo Example is named as: lpuart_echo_s32k144

				
					/*
 * Copyright 2020 NXP
 * All rights reserved.
 *
 * NXP Confidential. This software is owned or controlled by NXP and may only be
 * used strictly in accordance with the applicable license terms. By expressly
 * accepting such terms or by downloading, installing, activating and/or otherwise
 * using the software, you are agreeing that you have read, and that you agree to
 * comply with and are bound by, such license terms. If you do not agree to be
 * bound by the applicable license terms, then you may not retain, install,
 * activate or otherwise use the software. The production use license in
 * Section 2.3 is expressly granted for this software.
 */
/* ###################################################################
**     Filename    : main.c
**     Project     : lpuart_echo_s32k144
**     Processor   : S32K144
**     Version     : Driver 01.00
**     Compiler    : GNU C Compiler
**     Date/Time   : 2020-02-20, 12:27, # CodeGen: 1
**     Abstract    :
**         Main module.
**         This module contains user's application code.
**     Settings    :
**     Contents    :
**         No public methods
**
** ###################################################################*/
/*!
** @file main.c
** @version 01.00
** @brief
**         Main module.
**         This module contains user's application code.
*/
/*!
**  @addtogroup main_module main module documentation
**  @{
*/
/* MODULE main */

/* Including needed modules to compile this module/procedure */
#include "sdk_project_config.h"
#include <stdio.h>
#include <string.h>

volatile int exit_code = 0;

/* Welcome message displayed at the console */
#define welcomeMsg "This example is an simple echo using LPUART\r\n\
it will send back any character you send to it.\r\n\
The board will greet you if you send 'Hello Board'\r\
\nNow you can begin typing:\r\n"

/* Error message displayed at the console, in case data is received erroneously */
#define errorMsg "An error occurred! The application will stop!\r\n"

/* Timeout in ms for blocking operations */
#define TIMEOUT     500U

/* Receive buffer size */
#define BUFFER_SIZE 256U

/* Buffer used to receive data from the console */
uint8_t buffer[BUFFER_SIZE];
uint8_t bufferIdx;

/* UART rx callback for continuous reception, byte by byte */
void rxCallback(void *driverState, uart_event_t event, void *userData)
{
    /* Unused parameters */
    (void)driverState;
    (void)userData;

    /* Check the event type */
    if (event == UART_EVENT_RX_FULL)
    {
        /* The reception stops when newline is received or the buffer is full */
        if ((buffer[bufferIdx] != '\n') && (bufferIdx != (BUFFER_SIZE - 2U)))
        {
            /* Update the buffer index and the rx buffer */
            bufferIdx++;
            LPUART_DRV_SetRxBuffer(INST_LPUART_LPUART_1, &buffer[bufferIdx], 1U);
        }
    }
}

/*!
 \brief The main function for the project.
 \details The startup initialization sequence is the following:
 * - startup asm routine
 * - main()
 */
int main(void)
{
  /* Write your local variable definition here */
  status_t status;
  /* Declare a buffer used to store the received data */
  uint32_t bytesRemaining;

  /* Write your code here */
  /* For example: for(;;) { } */

  /* Initialize and configure clocks
   *     -    see clock manager component for details
   */
  CLOCK_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT,
                 g_clockManCallbacksArr, CLOCK_MANAGER_CALLBACK_CNT);
  CLOCK_SYS_UpdateConfiguration(0U, CLOCK_MANAGER_POLICY_AGREEMENT);

  /* Initialize pins
   *    -    See PinSettings component for more info
   */
  PINS_DRV_Init(NUM_OF_CONFIGURED_PINS0, g_pin_mux_InitConfigArr0);

  /* Initialize LPUART instance */
  LPUART_DRV_Init(INST_LPUART_LPUART_1, &lpUartState1, &lpUartInitConfig1);
  /* Install the callback for rx events */
  LPUART_DRV_InstallRxCallback(INST_LPUART_LPUART_1, rxCallback, NULL);
  /* Send a welcome message */
  LPUART_DRV_SendDataBlocking(INST_LPUART_LPUART_1, (uint8_t *)welcomeMsg, strlen(welcomeMsg), TIMEOUT);

  /* Infinite loop:
   *     - Receive data from user
   *     - Echo the received data back
   */
  while (1)
  {
      /* Receive and store data byte by byte until new line character is received,
       * or the buffer becomes full (256 characters received)
       */
      LPUART_DRV_ReceiveData(INST_LPUART_LPUART_1, buffer, 1U);
      /* Wait for transfer to be completed */
      while(LPUART_DRV_GetReceiveStatus(INST_LPUART_LPUART_1, &bytesRemaining) == STATUS_BUSY);

      /* Check the status */
      status = LPUART_DRV_GetReceiveStatus(INST_LPUART_LPUART_1, &bytesRemaining);

      if (status != STATUS_SUCCESS)
      {
          /* If an error occurred, send the error message and exit the loop */
          LPUART_DRV_SendDataBlocking(INST_LPUART_LPUART_1, (uint8_t *)errorMsg, strlen(errorMsg), TIMEOUT);
          break;
      }

      /* Append string terminator to the received data */
      bufferIdx++;
      buffer[bufferIdx] = 0U;

      /* If the received string is "Hello Board", send back "Hello World" */
      if(strcmp((char *)buffer, "Hello Board\n") == 0)
      {
          strcpy((char *)buffer, "Hello World\n");
      }

      /* Send the received data back */
      LPUART_DRV_SendDataBlocking(INST_LPUART_LPUART_1, buffer, bufferIdx, TIMEOUT);
      /* Reset the buffer index to start a new reception */
      bufferIdx = 0U;
  }

  for(;;) {
    if(exit_code != 0) {
      break;
    }
  }
  return exit_code;
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

/* END main */
/*!
 ** @}
 */

				
			
lpuart_echo_s32k144 demo code explanation:

Next blogs to read

SAR ADC Explained!

Why to learn about SAR ADC? SAR ADC is a standard AUTOSAR opts for. That’s why you see most of the automotive microcontrollers can be observed having SAR ADC and SAR stands for Successive Approximation Register. You can see the below picture which I extracted for verification of this fact. Microcontrollers which are verified: NXP S32K1xx Series NXP MPC5xxx Series STMicroelectronics SPC5 Series Renesas RH850 Series Infineon AURIX TC3xx Series Microchip PIC32 Series Why AUTOSAR likes SAR ADC over others? SAR ADC working is most suitable due to three major factors mentioned below: High Conversion Speed with Accuracy: SAR ADCs are fast to handle conversion like real-time sensor data conversion while holding its precision as it is. This conversion can be like throttle control, battery management, and other critical functions. Power Efficiency: Power consumption is one of the most important factors in any automotive application especially electric vehicles. SAR ADC consumes comparatively less power rather than ADC like FLASH ADC.  Scalability: SAR ADCs offer a trade-off between speed, resolution, and area, which is crucial in automotive designs where space and performance both matter. Comparing SAR ADC with Flash and Sigma-Delta ADC Flash ADC: The fastest type of ADC, converting signals in just one clock cycle. This speed comes at the cost of power consumption and size, as it requires one comparator per bit of resolution. Given the complex needs of automotive systems, this increased power draw and large footprint make Flash ADCs impractical for most real-time automotive control systems. Sigma-Delta ADC: Offers exceptional accuracy by oversampling the input signal and using noise-shaping techniques. However, its conversion speed is much slower compared to SAR ADCs. This makes it unsuitable for fast, real-time sensor data processing, though it shines in applications where high precision is needed, such as audio or pressure measurement. SAR ADC stands between these two, offering sufficient speed, accuracy, and power efficiency. This balance makes it the top choice for most automotive microcontroller designs, especially in safety-critical applications like engine control, where both speed and accuracy matter. How SAR ADC Works Sample & Hold (S/H) Block: This block holds the analog input signal steady while the ADC performs the conversion. The process begins by capturing the input voltage and freezing it momentarily to allow precise comparisons during the conversion. Comparator: The comparator checks the DAC’s output against the input signal at every stage of the conversion. It decides whether the next bit in the SAR register should be a ‘1’ or a ‘0’ based on whether the input signal is greater or lesser than the DAC output. SAR Register: This is a shift register that stores the output bit by bit as the conversion proceeds. The SAR register’s value evolves with each step of the approximation process, eventually containing the final digital equivalent of the input analog signal. DAC (Digital-to-Analog Converter): The DAC generates a voltage based on the digital bits already stored in the SAR register. The comparator then compares this DAC output with the input signal. The DAC’s resolution is crucial since it must match the resolution of the SAR ADC. Step-by-Step Example: How SAR ADC Calculates an Input Signal Example Setup: Reference Voltage (Vref): 5V Resolution: 4 bits (for simplicity) Input Voltage (Vin): 2.6V Author: Rohan Singhal

Read More »

Autosar MCAL layer ADC Driver API’s and data types explanation

API name: Adc_Init() void Adc_Init (const Adc_ConfigType* ConfigPtr) Role: Adc_Init() API initializes the ADC peripheral of the microcontroller. This API is universal and used across all automotive MCUs for initializing the ADC peripheral of the corresponding MCU. This API initializes the registers of ADC peripherals internally. So function definition of Adc_Init () would be different for different SoCs. But in applications across all automotive MCUs, this API name and syntax would be used to initialize the ADC peripheral according to the Adc_ConfigType structure. Working of this API:  This API calls the low-level functions that configure the ADC clock, prescaler, and trigger mode. This API initializes all the ADC instances, according to their configurations for ADC Hardware Unit. This API does not configure the pins of the MCU to analog pins. That part has to be done by the Port or MCU driver. Parameter passed: The parameter that is passed to this API is of Adc_ConfigType data type. Adc_ConfigType is a structure that contains the set of configuration parameters for initializing the ADC Driver and ADC HW units. The object of this data type is generated and defined by the configurator tool. We users don’t have to initialize this object. It is automatically configured based on the configuration we do on the GUI. We just have to send the object of Adc_ConfigType with ampersand (&) to this API. Chronology to use this API: This API is used in the beginning of main(). Just after the system clock and ADC pins are configured by their respective APIs. Return value: This function does not return anything. As it only initializes the internal peripheral registers. But just to check and verify the function, you can observe the changes in ADC HW unit registers just after executing this function. Syntax to use this API:  Adc_Init(&Adc_Config_VS_0); ADC Peripheral Registers affected by This API, with respect to S32K144 MCU using ElecronicsV3 Board:           API Name: Adc_EnableGroupNotification void Adc_EnableGroupNotification(Adc_GroupType Group) Role: This API, enables the notification feature when conversion of all channels of the ADC group is successfully converted.  Working of this API: After starting the ADC conversion either by software trigger or hardware trigger, the group notification function will be called only if its group notification is enabled. And that thing is done by this API. That’s why, in this API we just send one parameter, Group Number.  The ADC Group Notification callback function is called from the IRQ handler of ADC. ADC MCAL layer has a defined IRQ notification callback function, that is called when the IRQ handler of ADC is invoked upon successful conversion of ADC channels. And IRQ Notification callback function calls the group-specific notification callback and updates the Group Status to ADC Completed/ADC Stream Completed. For a single ADC hardware unit in a microcontroller, ADC IRQ is the same for all channels. So upon successful conversion of ADC of a channel IRQ handler notification is called, into which analysis is done that which channel of which group is completed and corresponding to that group notification callback is invoked. Parameter passed: The parameter that is passed to this API is of Adc_GroupType data type. Adc_GroupType is a typedef of uint16. It is just a numeric ID ( 1,2,3,4 etc), denoting the ADC group number. The values of Group IDs are generated and initialized by the code configurator tool. We users don’t have to initialize the group ID number. The group IDs are automatically macro-defined based on the GUI configuration tool. We just have to send the macro-defined group name in this API. Prerequisite: ADC should be used with Interrupts capability. If no interrupts are used, no notification capability will be invoked. The ADC Notification capability checkbox has to be checked in the AdcGeneral section of the ADC configurator tool. If this is not checked, the notification capability will not work. Make sure that we have configured the ADC Group Notification function in the configurator tool while configuring the ADC groups. // photo The name that would be written over here, the function of that name only will be created in generated files and we can define the function in the application code on how to use it and what to do. Chronology: This API is used just after the Adc_init () and before calling the application loop that involves the use of ADC conversion. Return value: This function does not return anything. As it only initializes the internal state to enable the notifications. Syntax to use this API: Adc_EnableGroupNotification(AdcGroup_0); API Name: Adc_StartGroupConversion() void Adc_StartGroupConversion(Adc_GroupType Group) Role: This API initializes the conversion of channels of the group which is triggered by software. This API starts the conversion of the ADC group which is configured to get triggered via a Software Trigger. Hardware Trigger ADC groups are not started via this API. After the usage of this API, the ADC conversion of channels that are referred by a single group would begin, and we can expect corresponding group notifications to be called. And to see the results of ADC conversion we can use the Adc_ReadGroup(). Working: This API initializes the internal ADC peripheral register of ADC channels of the group which has to be converted. It also writes on those peripheral registers which starts the ADC conversion by Software trigger. Parameters: The parameter that is passed to this API is of Adc_GroupType data type. Adc_GroupType is a typedef of uint16. It is just a numeric ID ( 1,2,3,4 etc), denoting the ADC group number. The values of Group IDs are generated and initialized by the configurator tool. We users don’t have to initialize the group ID number. The group IDs are automatically macro-defined based on the GUI configuration tool. We just have to send the macro-defined group name in this API. Prerequisite: The ADC module should be initialized with Adc_Init() API and ADC notifications of the group should be enabled. Syntax to this API: Adc_StartGroupConversion(AdcGroup_0); API Name: Adc_EnableHardwareTrigger() void Adc_EnableHardwareTrigger(Adc_GroupType Group) Role: This API initializes the conversion of channels of the group

Read More »

BootLoader EXPLAINED!

What is BootLoader(BL)? The bootloader is a small chunk of code that gets executed when the MCU powers on or resets. Its main function is to manage the initial startup process, including any necessary checks or updates before the main application code runs. How BootLoader(BL) is different from Application Software? Application Software is the function-specific code that you program to perform an MCU-based task(like controlling the motor, displaying information, etc,). But you must be thinking or saying, that your application software also behaves pretty same as bootloader cause everytime you start/reset MCU, it will restart your application code from the beginning, right? But there’s a quite crisp gap between this BootLoader and Application Software which can broaden your understanding of bootloader and application software. Let’s divide this crisp gap into 3 sub-domains: Memory Allocation: If I talk about memory occupied by BL and Application Software. The first thing would be where are these two located and in which memory, answering that, BL is located at the beginning of FLASH memory and it always has some reserved space only which is also write-protected(Thinking why write-protected because to prevent accidental overwriting during normal operation) whereas application software is also stored in FLASH memory only but it’s below the space allocated to BL. Execution Flow: Upon power-on or reset, the microcontroller’s program counter starts executing code from the beginning of the flash memory, where the bootloader is located. After the bootloader completes its tasks (like, checking for updates, and validating the application), it will typically jump to the application’s start address (which is known and fixed in the bootloader code) to begin executing the application. Update Process: A well-developed bootloader can help you even in handling firmware updates. It can receive new application code, erase the old code, and write the new code to memory. The application cannot typically update itself. It relies on the bootloader to manage this process. The application’s major focus is on running the device’s features and does not deal with the update or integrity checks. Let’s understand with a analogy Taking an analogy with our expertise field of automotive embedded systems. Considering ourselves as an OEM, we have been reported a problem. A software bug is discovered in the ECU that controls the braking system. This bug causes the braking system to occasionally misbehave under specific conditions, such as when the vehicle is driving downhill. Diagnosing this issue requires an early solution because the bug needs to be fixed quickly to ensure the safety of the vehicles on the road. During the update of this bug fixing, the vehicle’s battery drains, and the update process is interrupted: With a Bootloader: When power is restored, the bootloader detects that the application firmware is corrupted or incomplete. It can either retry the update automatically or revert to a backup firmware version, ensuring the vehicle remains operational and can be updated again. I agree in this condition driving is not safe, but at least the car can operate so that it can be driven to repair with full caution. Without a Bootloader: The ECU might attempt to boot the corrupted application, resulting in a system crash or malfunction. The vehicle may not start or may exhibit erratic behavior, requiring a technician to manually intervene and repair the ECU, which could be time-consuming and costly. EDIT: Just found another real-life example. As we all know windows push too much of FOTA(Firmware Over-The-Air), out of which some take too much time to restart and some get applied with restart. Now you must have seen this screen will update. This “DON’T TURN OFF YOUR COMPUTER” message is also displayed for the very same reason, how power disruption can lead to the bricking of your laptop. But you must be curious what exactly what will happen if I cut off the power supply, and power up the device again, right? As a bootloader function, this interruption will be acknowledged and the bootloader will roll back to the previous functional update. You might see below mention image on start-up. IMPORTANT: Avoid trying this at home, this maneuver costs us to retrieve the data but unfortunately all core OS files and applications were corrupted and no longer responded for usage, we tried some troubleshooting using the window troubleshooter, but it wasn’t effective. At last, we installed a completely fresh OS from scratch.  I would expect that you have completely understand the “WHY” and “WHAT” of BootLoader. Now, it’s time to understand the “HOW” of bootloader. Important Terminology in BootLoader Memory Management Memory management is quite an interesting concept that can also help in relating to real-world applications. As we understand the chronology of the working of a bootloader, which is firstly a bootloader will get which now will be followed by application software. Now basically there can be different executions of application software, let’s brief all possible ways: Executing Directly from Flash Memory: This specific condition is noticed in most of the embedded MCUs where the bootloader and application software are placed in FLASH memory only. Flash memory is non-volatile, meaning it retains data even when the power is off.  Copying to RAM Before Execution: In some cases, the application code might be copied from flash memory to RAM before execution. This approach is more common in systems where high performance is required or where the flash memory is slower than RAM. The major reason behind using RAM for execution, RAM is typically faster than flash memory. Overlaying: In systems with limited RAM, overlaying is a technique where only a portion of the program is loaded into RAM at any given time, with different parts of the program being loaded and unloaded as needed.  Virtual Memory Techniques: This specific technique is very common nowadays you must see that recent smartphones have this feature called RAM EXTENSION, where a portion of storage is accumulated to RAM for its R/W access. Parts of the application can be swapped in and out of physical RAM as needed. Such strategies

Read More »
Kunal Gupta
Author: Kunal Gupta

Author

Kunal Gupta

Leave a comment

Stay Updated With Us

Error: Contact form not found.

      Blog