[RTD400 LLD]K344 Center Aligned PWM Trigger ADC BCTU 
 1. Abstract 
     Not long ago, a customer's actual project needed to implement the high-level center point of the center-aligned PWM on the S32K3XX to trigger the ADC BCTU multi-channel sampling. This function requires the use of EMIOS, ADC, and BCTU. At the beginning, the customer was always unable to achieve the trigger at the center point of the PWM, and the trigger position was wrong. Later, After we give the explanation and analysis of the principles of the K3 related modules to the customer, as well as the guidance of the actual code configuration test, and the customer also achieved the required function. For the convenience of testing, PIT and UART printf functions are also added here. So here is a summary of the specific situation of this function, so that other customers who encounter similar functional requirements in the future can have a reference and can quickly build it. 
 The requirements are as shown in the figure below. In the up-down counting mode, the center-aligned PWM is output, and the trigger sampling of the ADC BTCU LIST is realized at the center point of the high level. This article will implement 1KHZ center-aligned PWM, 50% duty cycle, and high-level center point to achieve ADC BTCU LIST sampling on the official S32K344-EVB board, based on the RTD400 LLD version. 
   
  
 Fig 1 
 2. Center-aligned PWM center point trigger ADC principle 
       Regarding the principle, there is no suspense about ADC. You can directly refer to the previous article on ADC software and hardware triggering: 
 [RTD400 LLD]K344 ADC SW+HW trigger 
 This is the structure shown in Figure 2. After configuring ADC, BCTU and select the triggered EMIOS channel. When the relevant flag of EMIOS is generated, BCTU will be triggered. After the watermark is reached, a notification will be generated to store all LIST ADC channel values in the buffer, and then the ADC value can be printed out for easy viewing according to the situation.  
  
   Fig 2 
 In the current working condition, the logic that needs to be concerned about is mainly: how to generate center-aligned PWM, how to generate a flag at the high-level center point, and use this flag to trigger BTCU sampling. 
 2.1 How to generate center-aligned PWM 
      First, let’s take a look at the channel status of EMIOS and the channel status that supports center-aligned PWM. 
    
  Fig 3 
 The center-aligned PWM mode is: OPWMCB 
 As shown in Figure 1, we can see that the counter count mode in this mode is up and down. Let's take a look at the channel types that support OPWMCB. We can see that there is only type G.  
  
  Fig 4 
 So, what are the channels supported by Type G? See the figure below: 
  
  Fig 5 
 The mystery is solved. Only CH1-CH7 of eMIOS supports center-aligned PWM. 
 Well, then a new question arises. Can this mode directly generate a trigger signal at the center point of the PWM high level to trigger the ADC BCTU sampling? Answer: No! 
 The reason can be seen from Figure 1. In this PWM mode, there are two trigger flags generated when the up and down count comparison is completed. Therefore, if this mode channel is used to directly trigger ADC sampling, two triggers will be generated in one cycle, and both are on the edge of PWM. 
 Since OPWMCB cannot directly generate a trigger at the center point of PWM, how to achieve it? 
 In addition, open a dedicated channel, and the clock source is the same as OPWMCB, but that channel only counts to generate trigger flags and does not output any waveform. 
 2.2 How to generate PWM center point trigger flag 
   Key information: OPWMCB simultaneous clock source, only counting mode, or up and down MCB mode, only one channel is satisfied: ADC CH0, counter bus type also needs to be selected as counter_bus_B      
  
  Fig 6 
 Let's take a look at the trigger flag of MCB: 
  
  Fig 7 
 As you can see, it is perfect. The trigger mark is at the top of the upward and downward counting, which is exactly the center point of the high level of the same source PWM. 
 So, here, the specific channel arrangement has been determined in principle, two channels: EMIOS0_CH0 MCB mode pure counting and triggering the generation of flags to trigger ADC sampling; EMIOS0_CH1-CH7 is OPWMCB mode, simply outputting center-aligned PWM. 
 3. Software configuration and implementation 
       The software uses the S32K3 RTD400 version, and other versions have similar configurations! 
 3.1 Demo CT  module configuration 
 First, you need to prepare a K344 demo, and then configure the pins, clocks, and peripheral modules. The following will explain the configurations. The modules used are: 2 GPIOs, ADC, BCTU, EMIOS clock, EMIOS PWM, LPUART for printing logs, PIT for timed printing, Trgmux is configured but not used in this article, so I won't talk about it!      
 3.1.1 Pin configuation 
       The pins used are as follows: 
  
  Fig 8 
 The two GPIOs are used to toggle in BCTU trigger notification and BCTU watermark notification respectively, to test the waveform positioning. ADC1_s10 pin is used for ADC1, but this article mainly uses BCTU to combine ADC0. ADC0 does not add specific external pins, but uses internal signals. LPUART is used for printing, and EMIOS0_CH1 is used to generate center-aligned PWM waveforms. 
 3.1.2 Clock configuration 
      The clocks that need to be paid attention to here are: ADC clock, EMIOS clock, UART clock, and PIT clock. 
  
  Fig 9 
 These clocks will be used later when configuring specific timing periods and baud rates. 
 3.1.3 Peripheral configuration 
       The peripherals used mainly include the following modules: 
  
  Fig 10 
 (1)Adc_Sar_Ip 
 The configuration of the ADC0 module uses the one shot mode, that is, one conversion is generated after one trigger. In this way, the timer EMIOS channel can be used to generate trigger conditions to trigger a conversion. Pre-sampling is enabled to prevent the residual charge of the sampled value of the previous channel from affecting the result of the current channel and causing deviation of the ADC value. The trigger mode uses BCTU. For the configuration of Adc prescaler value and calibration prescale, the frequency division requirements in RM need to be met. The final configuration is as follows: 
  
  Fig 11  
  
   Fig 12 
 (2)Bctu_Ip 
 The configuration of Bctu_Ip needs to select the right EMIOS channel to use. From the above principle, we can know that EMIOS0_CH0 is used, and then LIST mode is adopted. For ADC0, three internal channels are currently added to the LIST: BANDGAP, VREFL, VREFH. The specific configuration is as follows: 
  
  Fig 13  
  
  Fig 14 
 (3)Emios_Mcl_Ip 
 Here we need to consider the PWM cycle that needs to be generated. The goal is to generate a 1Khz center-aligned PWM, so the EMIOS mode is the MCB up-down counting mode. 
 The clock cycle formula for MCB is: (2 x AS1) – 2. 
 So for the EMIOS0 clock source of 160Mhz, what is the corresponding 1KHZ counter? First do 160 divide to get 1Mhz, and then calculate according to (2 x AS1) – 2: 
 (2 x AS1) – 2 = 1Mhz/1KHZ=>AS1=1000/2 +1= 501. 
 The configuration is as follows: 
  
  Fig 15 
 (3)Emios_Pwm 
 The previous principle analysis shows that the PWM uses the OPWMCB center-aligned mode PWM, the clock comes from the counter bus B of EMIOS CH0, then the period is 1khz, and the duty cycle is 50%. The specific configuration is as follows: 
  
  Fig 16 
 (4)Lpuart_Uart 
 Configure UART6 clock baud rate to 115200.  
  
  Fig 17 
 (5)Pit  
  
  Fig 18 
 (6)IntCtrl_Ip 
 The interrupt configuration is as follows, and it can actually be configured via code. 
  
  Fig 19 
 3.2 Main code situation 
 After the configuration is completed, the output of the center PWM and the sampling of the ADC BCTU can be realized by calling the following code in main. 
 #include "Clock_Ip.h"
#include "IntCtrl_Ip.h"
#include "Adc_Sar_Ip.h"
#include "Bctu_Ip.h"
#include "Siul2_Port_Ip.h"
#include "Pit_Ip.h"
#include "Siul2_Dio_Ip.h"
#include "Trgmux_Ip.h"
#include "Lpuart_Uart_Ip.h"
#include "Lpuart_Uart_Ip_Irq.h"
#include "string.h"
#include "stdio.h"
#include "retarget.h"
#include "Emios_Mcl_Ip.h"
#include "Emios_Pwm_Ip.h"
/* PIT instance used - 0 */
#define PIT_INST_0 0U
/* PIT Channel used - 0 */
#define CH_0 0U
/* PIT time-out period - equivalent to 1s */
#define PIT_PERIOD 40000000
static volatile uint8 toggleLed = 0U;
#ifdef ADC_3V3_VREF_SELECTED
#define ADC_BANDGAP         5980U /* Vbandgap ~ 1.2V on 14 bits resolution, 3.3V VrefH */
#else
#define ADC_BANDGAP         3932U /* Vbandgap ~ 1.2V on 14 bits resolution, 5V VrefH */
#endif
#define ADC_SAR_USED_CH                 48U /* Internal Bandgap Channel */
#define BCTU_USED_SINGLE_TRIG_IDX       0U
#define BCTU_USED_FIFO_IDX              0U
#define BCTU_FIFO_WATERMARK             3U
#define ADC_TOLERANCE(x,y)              (((x > y) ? (x - y) : (y - x)) > 200U) /* Check that the data is within tolerated range */
#define LED_Q172_PIN            13u
#define LED_Q172_PORT           PTA_H_HALF
#define GPIO_PTA2_PIN           2u
#define GPIO_PTA2_PORT         PTA_L_HALF
#define EMIOS_INST0                  0U
volatile int exit_code = 0;
volatile boolean notif_triggered = FALSE;
volatile boolean notif_triggered1 = FALSE;
volatile uint16 data;
volatile uint16 data1;
volatile uint16 data_bctu[3];
#define UART_LPUART_INTERNAL_CHANNEL  6
#define WELCOME_MSG_1 "Hello, This message is sent via Uart!\r\n"
volatile Lpuart_Uart_Ip_StatusType lpuartStatus = LPUART_UART_IP_STATUS_ERROR;
uint32 remainingBytes;
uint32 T_timeout = 0xFFFFFF;
uint8* pBuffer;
extern void Adc_Sar_0_Isr(void);
extern void Bctu_0_Isr(void);
extern void Adc_Sar_1_Isr(void);
void AdcEndOfChainNotif(void)
{
    notif_triggered = TRUE;
    data = Adc_Sar_Ip_GetConvData(ADCHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, ADC_SAR_USED_CH);
    /* Checks the measured ADC data conversion */
}
void AdcEndOfChainNotif1(void)
{
    notif_triggered1 = TRUE;
    data1 = Adc_Sar_Ip_GetConvData(ADCHWUNIT_1_BOARD_INITPERIPHERALS_INSTANCE, 34);
    /* Checks the measured ADC data conversion */
}
void Pit0ch0Notification(void)
{
    toggleLed = 1U;
}
void BctuWatermarkNotif(void)
{
    uint8 idx;
    Siul2_Dio_Ip_WritePin(GPIO_PTA2_PORT, GPIO_PTA2_PIN, 1U);
    notif_triggered = TRUE;
    for (idx = 0u; idx < BCTU_FIFO_WATERMARK; idx++)
    {
    	data_bctu[idx] = Bctu_Ip_GetFifoData(BCTUHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, BCTU_USED_FIFO_IDX);
    }
    Siul2_Dio_Ip_WritePin(GPIO_PTA2_PORT, GPIO_PTA2_PIN, 0U);
}
void BcutTriggerNotif()
{
	Siul2_Dio_Ip_WritePin(LED_Q172_PORT, LED_Q172_PIN, 1U);
	Siul2_Dio_Ip_WritePin(LED_Q172_PORT, LED_Q172_PIN, 0U);
}
void TestDelay(uint32 delay);
void TestDelay(uint32 delay)
{
    static volatile uint32 DelayTimer = 0;
    while(DelayTimer < delay)
    {
        DelayTimer++;
    }
    DelayTimer = 0;
}
int main(void)
{
    StatusType status;
    uint8 Index;
    Clock_Ip_StatusType clockStatus;
    /* Initialize and configure drivers */
    clockStatus = Clock_Ip_Init(&Clock_Ip_aClockConfig[0]);
    while (clockStatus != CLOCK_IP_SUCCESS)
    {
        clockStatus = Clock_Ip_Init(&Clock_Ip_aClockConfig[0]);
    }
    Siul2_Port_Ip_Init(NUM_OF_CONFIGURED_PINS_PortContainer_0_BOARD_InitPeripherals, g_pin_mux_InitConfigArr_PortContainer_0_BOARD_InitPeripherals);
#if 1
    Bctu_Ip_Init(BCTUHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, &BctuHwUnit_0_BOARD_INITPERIPHERALS);
    status = (StatusType) Adc_Sar_Ip_Init(ADCHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, &AdcHwUnit_0_BOARD_InitPeripherals);
    while (status != E_OK);
    status = (StatusType) Adc_Sar_Ip_Init(ADCHWUNIT_1_BOARD_INITPERIPHERALS_INSTANCE, &AdcHwUnit_1_BOARD_InitPeripherals);
    while (status != E_OK);
    /* set PIT 0 interrupt */
    IntCtrl_Ip_Init(&IntCtrlConfig_0);
    IntCtrl_Ip_EnableIrq(PIT0_IRQn);
    /* Install and enable interrupt handlers */
    IntCtrl_Ip_InstallHandler(ADC0_IRQn, Adc_Sar_0_Isr, NULL_PTR);
    IntCtrl_Ip_InstallHandler(BCTU_IRQn, Bctu_0_Isr, NULL_PTR);
    IntCtrl_Ip_InstallHandler(ADC1_IRQn, Adc_Sar_1_Isr, NULL_PTR);
    IntCtrl_Ip_EnableIrq(ADC0_IRQn);
    IntCtrl_Ip_EnableIrq(BCTU_IRQn);
    IntCtrl_Ip_EnableIrq(ADC1_IRQn);
   // IntCtrl_Ip_EnableIrq(EMIOS0_5_IRQn);
    /* Call Calibration function multiple times, to mitigate instability of board source */
    for(Index = 0; Index <= 5; Index++)
    {
        status = (StatusType) Adc_Sar_Ip_DoCalibration(ADCHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE);
        if(status == E_OK)
        {
            break;
        }
    }
    for(Index = 0; Index <= 5; Index++)
    {
        status = (StatusType) Adc_Sar_Ip_DoCalibration(ADCHWUNIT_1_BOARD_INITPERIPHERALS_INSTANCE);
        if(status == E_OK)
        {
            break;
        }
    }
    Adc_Sar_Ip_EnableNotifications(ADCHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, ADC_SAR_IP_NOTIF_FLAG_NORMAL_ENDCHAIN | ADC_SAR_IP_NOTIF_FLAG_INJECTED_ENDCHAIN);
    Adc_Sar_Ip_EnableNotifications(ADCHWUNIT_1_BOARD_INITPERIPHERALS_INSTANCE, ADC_SAR_IP_NOTIF_FLAG_NORMAL_ENDCHAIN | ADC_SAR_IP_NOTIF_FLAG_INJECTED_ENDCHAIN);
    /* Start a SW triggered normal conversion on ADC_SAR */
    Adc_Sar_Ip_StartConversion(ADCHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, ADC_SAR_IP_CONV_CHAIN_NORMAL);
    /* Wait for the notification to be triggered and read the data */
    while (notif_triggered != TRUE);
    notif_triggered = FALSE;
    /* Start a SW triggered injected conversion on ADC_SAR */
    Adc_Sar_Ip_StartConversion(ADCHWUNIT_0_BOARD_INITPERIPHERALS_INSTANCE, ADC_SAR_IP_CONV_CHAIN_INJECTED);
    /* Wait for the notification to be triggered and read the data */
    while (notif_triggered != TRUE);
    notif_triggered = FALSE;
#endif
    /* Initialize PIT instance 0 - Channel 0 */
    Pit_Ip_Init(PIT_INST_0, &PIT_0_InitConfig_PB_BOARD_InitPeripherals);
    /* Initialize channel 0 */
    Pit_Ip_InitChannel(PIT_INST_0, PIT_0_CH_0);
    /* Enable channel interrupt PIT_0 - CH_0 */
    Pit_Ip_EnableChannelInterrupt(PIT_INST_0, CH_0);
    /* Start channel CH_0 */
    Pit_Ip_StartChannel(PIT_INST_0, CH_0, PIT_PERIOD);
   // Trgmux_Ip_Init(&Trgmux_Ip_xTrgmuxInitPB);//
    Lpuart_Uart_Ip_Init(UART_LPUART_INTERNAL_CHANNEL, &Lpuart_Uart_Ip_xHwConfigPB_6_BOARD_INITPERIPHERALS);
    Emios_Mcl_Ip_Init(EMIOS_INST0, &Emios_Mcl_Ip_0_Config_BOARD_INITPERIPHERALS);
    Emios_Pwm_Ip_InitChannel(EMIOS_PWM_IP_BOARD_INITPERIPHERALS_I0_CH1_CFG, &Emios_Pwm_Ip_BOARD_InitPeripherals_I0_Ch1);
    printf("S32K344 PIT TRIGMUX ADC demo RTD400.\r\n");
    /* Uart_AsyncSend transmit data */
    lpuartStatus = Lpuart_Uart_Ip_AsyncSend(UART_LPUART_INTERNAL_CHANNEL, (const uint8 *) WELCOME_MSG_1, strlen(WELCOME_MSG_1));
    /* Check for no on-going transmission */
    do
    {
        lpuartStatus = Lpuart_Uart_Ip_GetTransmitStatus(UART_LPUART_INTERNAL_CHANNEL, &remainingBytes);
    } while (LPUART_UART_IP_STATUS_BUSY == lpuartStatus && 0 < T_timeout--);
    Siul2_Dio_Ip_WritePin(GPIO_PTA2_PORT, GPIO_PTA2_PIN, 0U);
    while(1)
    {
#if 1
        if( toggleLed == 1)
        {
      	  toggleLed = 0;
  
      	  printf("ADC0_bandgap    ch48 data_bctu = %d .\r\n", data_bctu[0]);
      	  printf("ADC0_vrefl       ch54 data_bctu = %d .\r\n", data_bctu[1]);
      	  printf("ADC0_vrefh       ch55 data_bctu = %d .\r\n", data_bctu[2]);
        }
#endif
    }
    return exit_code;
}
 
 3.3 Test result 
    The test results include two parts: the printed results show the ADC sampling value, and the relationship between the PWM output and the BCTU trigger position. The printed results are as follows, and you can see that the values of the three different ADC channels are correct: 
  
  Fig 20 
 PWM wave test result is: 
 Ch1:PTB13 PWM 
 Ch2:PTA29 BCTU trigger notification 
 Ch3: PTA2 BCTU watermark notification 
  
   Fig 21 
 From the figure, we can see that the first BCTU trigger is at the center point of the PWM high level, and the BCTU watermark notification is used to store data after the ADC sampling is triggered three times. 
 Here is another waveform to view the PWM cycle: 
  
  Fig 22 
 It can be seen that the PWM period is 1Khz, the duty cycle is 50%, and the first trigger of BCTU is at the center of the PWM high pulse. 
 4. Summary and trip description 
 Through the above configuration, the RTD400 LLD method was finally used on the S32K344-EVB to implement a 1Khz, 50% duty cycle center-aligned PWM, and the high pulse center position triggered the ADC BCTU LIST acquisition. 
 The trip encountered were my own configuration problems. At the beginning, I mistakenly thought that the watermark configuration value was consistent with the number of LIST channels. In fact, the watermark trigger is triggered only when the watermark value is exceeded. Therefore, if it is a 3-channel LIST, the watermark needs to be configured as 2, not 3. If it is 3, the test waveform is as follows: 
  
 Fig 23 
 As you can see, the watermark notification is actually generated after it is triggered 4 times. 
 Therefore, remember to match the FIFO number of the watermark. 
 Attachment: 
 1. S32K344_centerPWM1k_TRIGMUX_BCTUHWLIST_EMIOS_ADC_printf_RTD400.zip 
 Center PWM triggered BCTU LIST 
   
 2.S32K344_CPWM1k_TRIGMUX_BCTUHWLIST_EMIOSch4_ADC_printf_RTD400.zip 
 PWM emios0_ch4 falling edge trigger the BCTU, for the comment situation. 
   
 3. Another method of center trigger: MCB_Counter up. 
 Emios0_CH0 as the counter bus 
 EMIOS0_CH1 as the PWM output 
 EMIOS0_CH2 as the center trigger, but no PWM output, OPWMB 
 S32K344_NCPWM1k1_TRIGMUX_BCTUHWLIST_EMIOS_ADC_printf_RTD400.zip 
 
        
        View full article